/**********************************************************************
Copyright (c) 2006 Andy Jefferson and others. All rights reserved.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
Contributors:
...
**********************************************************************/
package org.jpox.jpa.metadata;
import org.xml.sax.Attributes;
import org.xml.sax.EntityResolver;
import org.xml.sax.SAXException;
import org.jpox.metadata.AbstractClassMetaData;
import org.jpox.metadata.AbstractMemberMetaData;
import org.jpox.metadata.ClassMetaData;
import org.jpox.metadata.ClassPersistenceModifier;
import org.jpox.metadata.ColumnMetaData;
import org.jpox.metadata.DiscriminatorMetaData;
import org.jpox.metadata.DiscriminatorStrategy;
import org.jpox.metadata.ElementMetaData;
import org.jpox.metadata.EventListenerMetaData;
import org.jpox.metadata.FieldPersistenceModifier;
import org.jpox.metadata.FileMetaData;
import org.jpox.metadata.IdentityStrategy;
import org.jpox.metadata.IdentityType;
import org.jpox.metadata.InheritanceMetaData;
import org.jpox.metadata.InheritanceStrategy;
import org.jpox.metadata.JoinMetaData;
import org.jpox.metadata.KeyMetaData;
import org.jpox.metadata.MetaData;
import org.jpox.metadata.MetaDataManager;
import org.jpox.metadata.OrderMetaData;
import org.jpox.metadata.PackageMetaData;
import org.jpox.metadata.QueryMetaData;
import org.jpox.metadata.QueryResultMetaData;
import org.jpox.metadata.SequenceMetaData;
import org.jpox.metadata.TableGeneratorMetaData;
import org.jpox.metadata.UniqueMetaData;
import org.jpox.metadata.VersionMetaData;
import org.jpox.metadata.VersionStrategy;
import org.jpox.metadata.xml.AbstractMetaDataHandler;
import org.jpox.util.StringUtils;
import org.jpox.util.JPOXLogger;
/**
* Parser handler for JPA MetaData.
* Implements DefaultHandler and handles the extracting of MetaData for JPA
* from the XML elements/attributes. This class simply constructs the MetaData
* representation mirroring what is in the MetaData file. It has no knowledge
* of the class(es) that it represents, simply the information in the MetaData
* file. The knowledge of the classes is imposed on the representation at a
* later stage where necessary.
* <P>Operates the parse process using a Stack. MetaData components are added
* to the stack as they are encountered and created. They are then popped off
* the stack when the end element is encountered.</P>
*
* @since 1.1
* @version $Revision: 1.1 $
*/
public class JPAMetaDataHandler extends AbstractMetaDataHandler
{
/** Default package name for the entity-mappings (if any). */
String defaultPackageName = null;
/** Flag for whether the MetaData in this file is "metadata-complete" */
boolean metaDataComplete = false;
/** Default value for whether to cascade-persist. */
boolean defaultCascadePersist = false;
/** Whether this file is using property access. */
boolean propertyAccess = false;
/** Current query result entity (persistent class), during parse process. */
String queryResultEntityName = null;
/**
* Constructor. Protected to prevent instantiation.
* @param mgr the metadata manager
* @param filename The name of the file to parse
* @param resolver Entity Resolver to use (null if not available)
*/
public JPAMetaDataHandler(MetaDataManager mgr, String filename, EntityResolver resolver)
{
super(mgr, filename, resolver);
metadata = new FileMetaData(filename, mgr, null, null);
pushStack(metadata); // Start with FileMetaData on the stack
}
/**
* Utility to create a new class component.
* @param pmd The parent PackageMetaData
* @param attrs The attributes
* @param embeddedOnly Whether this class is embedded-only
* @return The ClassMetaData
*/
protected ClassMetaData newClassObject(PackageMetaData pmd, Attributes attrs, boolean embeddedOnly)
{
String className = getAttr(attrs, "class");
if (className.indexOf('.') > 0)
{
// Strip off any package info
className = className.substring(className.lastIndexOf('.')+1);
}
ClassMetaData cmd = mgr.getMetaDataFactory().newClassObject(pmd,
className,
embeddedOnly ? IdentityType.NONDURABLE.toString() : IdentityType.APPLICATION.toString(),
null, // PK class specified on <id-class>
"true", // All classes require an extent
"true", // All classes are detachable in JPA
"" + embeddedOnly,
ClassPersistenceModifier.PERSISTENCE_CAPABLE.toString(), // JPA only has "persistence-capable"
null, // pc-superclass is not needed
null, // Catalog specified on <table>
null, // Schema specified on <table>
null, // Table specified on <table>
getAttr(attrs, "name")); // Entity name for use by JPQL
String classMetaDataComplete = getAttr(attrs, "metadata-complete");
if (metaDataComplete || (classMetaDataComplete != null && classMetaDataComplete.equalsIgnoreCase("true")))
{
// Ignore any annotations since either the class or the whole file is "metadata-complete"
cmd.setMetaDataComplete();
}
return cmd;
}
/**
* Utility to create a new field/property component and add it to the class as required.
* If the field/property already exists
* @param acmd The parent class MetaData
* @param attrs The attributes
* @return The FieldMetaData/PropertyMetaData
*/
protected AbstractMemberMetaData newFieldObject(AbstractClassMetaData acmd, Attributes attrs)
{
String fetch = getAttr(attrs, "fetch");
String dfg = "true";
if (fetch != null && fetch.equalsIgnoreCase("LAZY"))
{
dfg = "false";
}
AbstractMemberMetaData mmd = null;
mmd = acmd.getMetaDataForMember(getAttr(attrs, "name"));
if (mmd != null)
{
// Member exists, so add all attributes required
if (dfg != null)
{
mmd.setDefaultFetchGroup(dfg.equals("true") ? true : false);
}
String depString = getAttr(attrs,"dependent");
if (!StringUtils.isWhitespace(depString))
{
mmd.setDependent((depString.trim().equalsIgnoreCase("true") ? true : false));
}
mmd.setMappedBy(getAttr(attrs,"mapped-by"));
String loadFg = getAttr(attrs,"load-fetch-group");
if (!StringUtils.isWhitespace(loadFg))
{
mmd.setLoadFetchGroup(loadFg);
}
}
else
{
if (propertyAccess)
{
mmd = mgr.getMetaDataFactory().newPropertyObject(acmd,
getAttr(attrs,"name"),
null, // primary-key is specified using "id"
FieldPersistenceModifier.PERSISTENT.toString(), // transient go through a different method
dfg,
null, // No such concept in JPA
null, // Specified via "embedded"
null, // Specified via "lob"
getAttr(attrs,"dependent"),
getAttr(attrs,"mapped-by"),
null, // Specified via "column"
null, // Specified via "secondary-table" or "join-table"
null, // Specified via "secondary-table" or "join-table"
null, // Specified via "secondary-table" or "join-table"
null, // JPA has no delete-action
null, // JPA has no index option
null, // unique is specified elsewhere
null, // JPA has no recursion depth
getAttr(attrs,"load-fetch-group"),
null, // value strategy is specified elsewhere
null, // sequence is specified elsewhere
null, // JPA has no field-type
null); // JPA has no field-name
}
else
{
mmd = mgr.getMetaDataFactory().newFieldObject(acmd,
getAttr(attrs,"name"),
null, // primary-key is specified using "id"
FieldPersistenceModifier.PERSISTENT.toString(), // transient go through a different method
dfg,
null, // No such concept in JPA
null, // Specified via "embedded"
null, // Specified via "lob"
getAttr(attrs,"dependent"),
getAttr(attrs,"mapped-by"),
null, // Specified via "column"
null, // Specified via "secondary-table" or "join-table"
null, // Specified via "secondary-table" or "join-table"
null, // Specified via "secondary-table" or "join-table"
null, // JPA has no delete-action
null, // JPA has no index option
null, // unique is specified elsewhere
null, // JPA has no recursion depth
getAttr(attrs,"load-fetch-group"),
null, // value strategy is specified elsewhere
null, // sequence is specified elsewhere
null); // JPA has no field-type
}
acmd.addMember(mmd);
}
if (defaultCascadePersist)
{
// This file has <persistence-unit-defaults> set to cascade-persist all fields
mmd.setCascadePersist(true);
}
return mmd;
}
/**
* Utility to create a new primary key field/property component.
* @param acmd The parent class MetaData
* @param attrs Attributes of the "id" element
* @return The FieldMetaData/PropertyMetaData
*/
protected AbstractMemberMetaData newPKFieldObject(AbstractClassMetaData acmd, Attributes attrs)
{
AbstractMemberMetaData mmd = null;
mmd = acmd.getMetaDataForMember(getAttr(attrs, "name"));
if (mmd != null)
{
// Member exists, so mark as PK
mmd.setPrimaryKey();
}
else
{
// Create new property/field
if (propertyAccess)
{
mmd = mgr.getMetaDataFactory().newPropertyObject(acmd, getAttr(attrs, "name"), "true",
FieldPersistenceModifier.PERSISTENT.toString(), null, null, null, null, null, null,
null, null, null, null, null, null, null, null, null, null, null, null, null);
}
else
{
mmd = mgr.getMetaDataFactory().newFieldObject(acmd, getAttr(attrs, "name"), "true",
FieldPersistenceModifier.PERSISTENT.toString(), null, null, null, null, null, null,
null, null, null, null, null, null, null, null, null, null, null, null);
}
if (defaultCascadePersist)
{
// This file has <persistence-unit-defaults> set to cascade-persist all fields
mmd.setCascadePersist(true);
}
acmd.addMember(mmd);
}
return mmd;
}
/**
* Utility to create a new transient field/property component.
* @param md The parent MetaData
* @param name Name of the transient field
* @return The FieldMetaData/PropertyMetaData
*/
protected AbstractMemberMetaData newTransientFieldObject(MetaData md, String name)
{
AbstractMemberMetaData mmd = null;
if (propertyAccess)
{
mmd = mgr.getMetaDataFactory().newPropertyObject(md, name, null,
FieldPersistenceModifier.NONE.toString(), null, null, null, null, null, null, null,
null, null, null, null, null, null, null, null, null, null, null, null);
}
else
{
mmd = mgr.getMetaDataFactory().newFieldObject(md, name, null,
FieldPersistenceModifier.NONE.toString(), null, null, null, null, null, null, null,
null, null, null, null, null, null, null, null, null, null, null);
}
if (defaultCascadePersist)
{
// This file has <persistence-unit-defaults> set to cascade-persist all fields
mmd.setCascadePersist(true);
}
return mmd;
}
/**
* Utility to create a new field entry for a field/property in a superclass.
* @param md The parent MetaData
* @param attrs Attributes of the "id" element
* @return The FieldMetaData/PropertyMetaData
*/
protected AbstractMemberMetaData newOverriddenFieldObject(MetaData md, Attributes attrs)
{
AbstractMemberMetaData mmd = null;
if (propertyAccess)
{
mmd = mgr.getMetaDataFactory().newPropertyObject(md, "#UNKNOWN." + getAttr(attrs, "name"), null,
FieldPersistenceModifier.PERSISTENT.toString(), null, null, null, null, null, null, null, null,
null, null, null, null, null, null, null, null, null, null, null);
}
else
{
mmd = mgr.getMetaDataFactory().newFieldObject(md, "#UNKNOWN." + getAttr(attrs, "name"), null,
FieldPersistenceModifier.PERSISTENT.toString(), null, null, null, null, null, null, null, null,
null, null, null, null, null, null, null, null, null, null);
}
if (defaultCascadePersist)
{
// This file has <persistence-unit-defaults> set to cascade-persist all fields
mmd.setCascadePersist(true);
}
return mmd;
}
/**
* Handler method called at the start of an element.
* @param uri URI of the tag
* @param localName Local name
* @param qName Element name
* @param attrs Attributes for this element
* @throws SAXException in parsing errors
*/
public void startElement(String uri, String localName, String qName, Attributes attrs)
throws SAXException
{
if (JPOXLogger.METADATA.isDebugEnabled())
{
StringBuffer sb = new StringBuffer();
sb.append("<" + qName);
for (int i=0; i<attrs.getLength(); i++)
{
sb.append(" ");
sb.append(attrs.getQName(i)).append("=\"").append(attrs.getValue(i)).append("\"");
}
sb.append(">");
JPOXLogger.METADATA.debug(LOCALISER.msg("044034",sb.toString(), "" + stack.size()));
}
if (localName.length()<1)
{
localName = qName;
}
try
{
if (localName.equals("entity-mappings"))
{
FileMetaData filemd = (FileMetaData)getStack();
filemd.setType(FileMetaData.JPA_FILE);
}
else if (localName.equals("description"))
{
// Of no practical use so ignored
}
else if (localName.equals("persistence-unit-metadata"))
{
// Nothing to do - we use subelements
}
else if (localName.equals("xml-mapping-metadata-complete"))
{
// All classes in the file are complete without any annotations
metaDataComplete = true;
}
else if (localName.equals("persistence-unit-defaults"))
{
// Nothing to do - we use subelements
}
else if (localName.equals("package"))
{
// Processed in endElement()
}
else if (localName.equals("schema"))
{
// Processed in endElement()
}
else if (localName.equals("catalog"))
{
// Processed in endElement()
}
else if (localName.equals("access"))
{
// Processed in endElement()
}
else if (localName.equals("sequence-generator"))
{
// Sequence generator (add to the default package whether they are specified there are not)
MetaData md = getStack();
String initValue = getAttr(attrs, "initial-value");
if (StringUtils.isWhitespace(initValue))
{
initValue = "1"; // JPA default
}
String allocSize = getAttr(attrs, "allocation-size");
if (StringUtils.isWhitespace(allocSize))
{
allocSize = "50"; // JPA default
}
SequenceMetaData seqmd = new SequenceMetaData(md, getAttr(attrs, "name"),
getAttr(attrs, "sequence-name"), null, null, initValue, allocSize);
PackageMetaData pmd = null;
if (defaultPackageName != null)
{
pmd = ((FileMetaData)metadata).getPackage(defaultPackageName);
}
else
{
if (((FileMetaData)metadata).getNoOfPackages() > 0)
{
pmd = ((FileMetaData)metadata).getPackage(0);
}
else
{
// Add a dummy (root) package to hold our sequences since no default package name set
pmd = new PackageMetaData((FileMetaData)metadata, "", null, null);
((FileMetaData)metadata).addPackage(pmd);
}
}
pmd.addSequence(seqmd);
}
else if (localName.equals("table-generator"))
{
// Table generator (add to the default package whether they are specified there or not)
MetaData md = getStack();
TableGeneratorMetaData tgmd = new TableGeneratorMetaData(md, getAttr(attrs, "name"),
getAttr(attrs, "table"), getAttr(attrs, "catalog"), getAttr(attrs, "schema"), getAttr(attrs, "pk-column-name"),
getAttr(attrs, "value-column-name"), getAttr(attrs, "pk-column-value"), getAttr(attrs, "initial-value"),
getAttr(attrs, "allocation-size"));
PackageMetaData pmd = null;
if (defaultPackageName != null)
{
pmd = ((FileMetaData)metadata).getPackage(defaultPackageName);
}
else
{
if (((FileMetaData)metadata).getNoOfPackages() > 0)
{
pmd = ((FileMetaData)metadata).getPackage(0);
}
else
{
// Add a dummy (root) package to hold our sequences since no default package name set
pmd = new PackageMetaData((FileMetaData)metadata, "", null, null);
((FileMetaData)metadata).addPackage(pmd);
}
}
pmd.addTableGenerator(tgmd);
}
else if (localName.equals("named-query"))
{
// Named JPQL query
MetaData md = getStack();
if (md instanceof FileMetaData)
{
FileMetaData filemd = (FileMetaData)md;
QueryMetaData qmd = new QueryMetaData(filemd, null, getAttr(attrs, "name"), "javax.jdo.query.JPQL",
null, null, null, null, null);
filemd.addQuery(qmd);
pushStack(qmd);
}
else if (md instanceof ClassMetaData)
{
ClassMetaData cmd = (ClassMetaData)md;
QueryMetaData qmd = new QueryMetaData(cmd, null, getAttr(attrs, "name"), "javax.jdo.query.JPQL",
null, null, null, null, null);
cmd.addQuery(qmd);
pushStack(qmd);
}
}
else if (localName.equals("named-native-query"))
{
// Named SQL query
MetaData md = getStack();
if (md instanceof FileMetaData)
{
FileMetaData filemd = (FileMetaData)md;
QueryMetaData qmd = new QueryMetaData(filemd, null, getAttr(attrs, "name"), "javax.jdo.query.SQL", null,
getAttr(attrs, "result-class"), getAttr(attrs, "result-set-mapping"), null, null);
filemd.addQuery(qmd);
pushStack(qmd);
}
else if (md instanceof ClassMetaData)
{
ClassMetaData cmd = (ClassMetaData)md;
QueryMetaData qmd = new QueryMetaData(cmd, null, getAttr(attrs, "name"), "javax.jdo.query.SQL", null,
getAttr(attrs, "result-class"), getAttr(attrs, "result-set-mapping"), null, null);
cmd.addQuery(qmd);
pushStack(qmd);
}
}
else if (localName.equals("sql-result-set-mapping"))
{
MetaData md = getStack();
if (md instanceof FileMetaData)
{
FileMetaData filemd = (FileMetaData)md;
QueryResultMetaData qrmd = new QueryResultMetaData(filemd, getAttr(attrs, "name"));
filemd.addQueryResultMetaData(qrmd);
pushStack(qrmd);
}
else if (md instanceof ClassMetaData)
{
ClassMetaData cmd = (ClassMetaData)md;
QueryResultMetaData qrmd = new QueryResultMetaData(cmd, getAttr(attrs, "name"));
cmd.addQueryResultMetaData(qrmd);
pushStack(qrmd);
}
}
else if (localName.equals("entity-result"))
{
// Add an entity (persistent class) mapping
QueryResultMetaData qrmd = (QueryResultMetaData)getStack();
queryResultEntityName = getAttr(attrs, "entity-class"); // Save this for any field-result that arrives
qrmd.addPersistentTypeMapping(queryResultEntityName,
null, // No field-column mappings info at this point
getAttr(attrs, "discriminator-column"));
}
else if (localName.equals("field-result"))
{
// Add a field-column mapping for the entity (persistent class)
QueryResultMetaData qrmd = (QueryResultMetaData)getStack();
qrmd.addMappingForPersistentTypeMapping(queryResultEntityName,
getAttr(attrs, "name"), getAttr(attrs, "column"));
}
else if (localName.equals("column-result"))
{
// Add a scalar column mapping
QueryResultMetaData qrmd = (QueryResultMetaData)getStack();
qrmd.addScalarColumn(getAttr(attrs, "name"));
}
else if (localName.equals("mapped-superclass"))
{
// New entity for this package
FileMetaData filemd = (FileMetaData)getStack();
String className = getAttr(attrs, "class");
String packageName = null;
if (className.indexOf('.') > 0)
{
// Fully-qualified so use package name from class
packageName = className.substring(0, className.lastIndexOf('.'));
}
PackageMetaData pmd = null;
if (packageName != null)
{
pmd = filemd.getPackage(packageName);
}
if (pmd == null)
{
if (packageName != null)
{
// Class fully qualified so add its package
pmd = new PackageMetaData(filemd, packageName, null, null);
}
else if (defaultPackageName != null)
{
// Use default package for entity-mappings
pmd = filemd.getPackage(defaultPackageName);
}
else
{
// Add root package
pmd = new PackageMetaData(filemd, "", null, null);
}
}
ClassMetaData cmd = newClassObject(pmd, attrs, false);
pmd.addClass(cmd);
// Set to use "subclass-table" since all subclasses inherit these fields
cmd.setInheritanceMetaData(new InheritanceMetaData(cmd, InheritanceStrategy.SUBCLASS_TABLE.toString()));
pushStack(cmd);
}
else if (localName.equals("query"))
{
// Processed in endElement()
}
else if (localName.equals("entity"))
{
// New entity for this package
FileMetaData filemd = (FileMetaData)getStack();
String className = getAttr(attrs, "class");
String packageName = null;
if (className.indexOf('.') > 0)
{
// Fully-qualified so use package name from class
packageName = className.substring(0, className.lastIndexOf('.'));
}
PackageMetaData pmd = null;
if (packageName != null)
{
pmd = filemd.getPackage(packageName);
}
if (pmd == null)
{
if (packageName != null)
{
// Class fully qualified so add its package
pmd = new PackageMetaData(filemd, packageName, null, null);
}
else if (defaultPackageName != null)
{
// Use default package for entity-mappings
pmd = filemd.getPackage(defaultPackageName);
}
else
{
// Add root package
pmd = new PackageMetaData(filemd, "", null, null);
}
}
ClassMetaData cmd = newClassObject(pmd, attrs, false);
pmd.addClass(cmd);
pushStack(cmd);
}
else if (localName.equals("embeddable"))
{
// New embedded-only entity for this package
FileMetaData filemd = (FileMetaData)getStack();
String className = getAttr(attrs, "class");
String packageName = null;
if (className.indexOf('.') > 0)
{
// Fully-qualified so use package name from class
packageName = className.substring(0, className.lastIndexOf('.'));
}
PackageMetaData pmd = null;
if (packageName != null)
{
pmd = filemd.getPackage(packageName);
}
if (pmd == null)
{
if (packageName != null)
{
// Class fully qualified so add its package
pmd = new PackageMetaData(filemd, packageName, null, null);
}
else if (defaultPackageName != null)
{
// Use default package for entity-mappings
pmd = filemd.getPackage(defaultPackageName);
}
else
{
// Add root package
pmd = new PackageMetaData(filemd, "", null, null);
}
}
ClassMetaData cmd = newClassObject(pmd, attrs, true);
pmd.addClass(cmd);
pushStack(cmd);
}
else if (localName.equals("attributes"))
{
// Nothing to do since is just a holder of other elements
}
else if (localName.equals("embeddable-attributes"))
{
// Nothing to do since is just a holder of other elements
}
else if (localName.equals("id-class"))
{
// Identity class
ClassMetaData cmd = (ClassMetaData)getStack();
cmd.setObjectIdClass(getAttr(attrs, "class"));
}
else if (localName.equals("inheritance"))
{
// Inheritance - only for root class
ClassMetaData cmd = (ClassMetaData)getStack();
String strategy = getAttr(attrs, "strategy");
String strategyType = null;
if (strategy.equalsIgnoreCase("JOINED"))
{
strategyType = InheritanceStrategy.NEW_TABLE.toString();
}
else if (strategy.equalsIgnoreCase("TABLE_PER_CLASS"))
{
strategyType = InheritanceStrategy.COMPLETE_TABLE.toString();
}
else
{
// SINGLE_TABLE (default), so implies nothing needs setting since thats the default
}
InheritanceMetaData inhmd = new InheritanceMetaData(cmd, getAttr(attrs, strategyType));
inhmd.setStrategyForTree(strategy.toUpperCase());
cmd.setInheritanceMetaData(inhmd);
}
else if (localName.equals("table"))
{
// Table for this entity
ClassMetaData cmd = (ClassMetaData)getStack();
cmd.setCatalog(getAttr(attrs, "catalog"));
cmd.setSchema(getAttr(attrs, "schema"));
cmd.setTable(getAttr(attrs, "name"));
}
else if (localName.equals("secondary-table"))
{
// Join for this entity
ClassMetaData cmd = (ClassMetaData)getStack();
JoinMetaData joinmd = new JoinMetaData(cmd,
getAttr(attrs, "name"), getAttr(attrs, "catalog"), getAttr(attrs, "schema"),
null, null, null, null, null);
cmd.addJoin(joinmd);
pushStack(joinmd);
}
else if (localName.equals("primary-key-join-column"))
{
MetaData md = getStack();
if (md instanceof ClassMetaData)
{
// Join columns between PK of subclass table and PK of base class table
ClassMetaData cmd = (ClassMetaData)md;
ColumnMetaData colmd = new ColumnMetaData(cmd.getInheritanceMetaData().getJoinMetaData(),
getAttr(attrs, "name"),
getAttr(attrs, "referenced-column-name"), // Column in primary table
null, null, null, null, null, null, null, null, null, null, null);
cmd.getInheritanceMetaData().getJoinMetaData().addColumn(colmd);
}
else if (md instanceof JoinMetaData)
{
// Join columns between PK of secondary table and PK of primary table
JoinMetaData joinmd = (JoinMetaData)md;
ColumnMetaData colmd = new ColumnMetaData(joinmd,
getAttr(attrs, "name"),
getAttr(attrs, "referenced-column-name"), // Column in primary table
null, null, null, null, null, null, null, null, null, null, null);
joinmd.addColumn(colmd);
}
}
else if (localName.equals("id"))
{
// Identity field
ClassMetaData cmd = (ClassMetaData)getStack();
AbstractMemberMetaData mmd = newPKFieldObject(cmd, attrs);
pushStack(mmd);
}
else if (localName.equals("embedded-id"))
{
// Embedded identity field
ClassMetaData cmd = (ClassMetaData)getStack();
AbstractMemberMetaData mmd = newPKFieldObject(cmd, attrs);
pushStack(mmd);
}
else if (localName.equals("basic"))
{
// Basic field
AbstractClassMetaData cmd = (AbstractClassMetaData)getStack();
AbstractMemberMetaData mmd = newFieldObject(cmd, attrs);
pushStack(mmd);
}
else if (localName.equals("lob"))
{
AbstractMemberMetaData fmd = (AbstractMemberMetaData)getStack();
fmd.setStoreInLob(); // Just mark it as to be stored in a "lob" and let the MetaData sort it out
}
else if (localName.equals("enumerated"))
{
// Processed elsewhere
}
else if (localName.equals("temporal"))
{
// Processed elsewhere
}
else if (localName.equals("transient"))
{
// Transient field
ClassMetaData cmd = (ClassMetaData)getStack();
AbstractMemberMetaData mmd = newTransientFieldObject(cmd, getAttr(attrs, "name"));
cmd.addMember(mmd);
pushStack(mmd);
}
else if (localName.equals("one-to-many"))
{
// 1-N field
ClassMetaData cmd = (ClassMetaData)getStack();
AbstractMemberMetaData mmd = newFieldObject(cmd, attrs);
mmd.setTargetClassName(getAttr(attrs, "target-entity"));
mmd.setOrdered();
if (mmd.getMappedBy() == null && mmd.getJoinMetaData() == null &&
!mgr.getOMFContext().getPersistenceConfiguration().getBooleanProperty("org.jpox.jpa.oneToManyUniFkRelations"))
{
// Strict JPA : 1-N uni with no join specified (yet) so add one (see JPA spec [9.1.24])
mmd.setJoinMetaData(new JoinMetaData(mmd, null, null, null, null, null, null, null, null));
}
pushStack(mmd);
}
else if (localName.equals("one-to-one"))
{
// 1-1 field
ClassMetaData cmd = (ClassMetaData)getStack();
AbstractMemberMetaData mmd = newFieldObject(cmd, attrs);
mmd.setTargetClassName(getAttr(attrs, "target-entity"));
pushStack(mmd);
}
else if (localName.equals("many-to-one"))
{
// N-1 field
ClassMetaData cmd = (ClassMetaData)getStack();
AbstractMemberMetaData mmd = newFieldObject(cmd, attrs);
mmd.setTargetClassName(getAttr(attrs, "target-entity"));
pushStack(mmd);
}
else if (localName.equals("many-to-many"))
{
// M-N field
ClassMetaData cmd = (ClassMetaData)getStack();
AbstractMemberMetaData mmd = newFieldObject(cmd, attrs);
mmd.setTargetClassName(getAttr(attrs, "target-entity"));
mmd.setOrdered();
if (mmd.getMappedBy() == null && mmd.getJoinMetaData() == null)
{
// M-N and no join specified (yet) so add one
mmd.setJoinMetaData(new JoinMetaData(mmd, null, null, null, null, null, null, null, null));
}
pushStack(mmd);
}
else if (localName.equals("map-key"))
{
// Key of a Map (field/property of the value class)
AbstractMemberMetaData fmd = (AbstractMemberMetaData)getStack();
String mappedByFieldName = getAttr(attrs, "name");
if (StringUtils.isWhitespace(mappedByFieldName))
{
mappedByFieldName = "#PK"; // Special value understood by MapMetaData.populate()
}
KeyMetaData keymd = new KeyMetaData(fmd, null, null, null, null, null, mappedByFieldName);
fmd.setKeyMetaData(keymd);
}
else if (localName.equals("order-by"))
{
// Processed in endElement()
}
else if (localName.equals("cascade"))
{
// Do nothing
}
else if (localName.equals("cascade-type"))
{
// Handled in elements below
}
else if (localName.equals("cascade-all"))
{
AbstractMemberMetaData mmd = (AbstractMemberMetaData)getStack();
mmd.setCascadePersist(true);
mmd.setCascadeUpdate(true);
mmd.setCascadeDelete(true);
mmd.setCascadeRefresh(true);
}
else if (localName.equals("cascade-persist"))
{
MetaData md = getStack();
if (md instanceof AbstractMemberMetaData)
{
AbstractMemberMetaData mmd = (AbstractMemberMetaData)md;
mmd.setCascadePersist(true);
}
else if (md instanceof FileMetaData)
{
// Specified at <persistence-unit-defaults>
defaultCascadePersist = true;
}
}
else if (localName.equals("cascade-merge"))
{
AbstractMemberMetaData mmd = (AbstractMemberMetaData)getStack();
mmd.setCascadeUpdate(true);
}
else if (localName.equals("cascade-remove"))
{
AbstractMemberMetaData mmd = (AbstractMemberMetaData)getStack();
mmd.setCascadeDelete(true);
}
else if (localName.equals("cascade-refresh"))
{
AbstractMemberMetaData mmd = (AbstractMemberMetaData)getStack();
mmd.setCascadeRefresh(true);
}
else if (localName.equals("version"))
{
if (getStack() instanceof ClassMetaData)
{
// Version field
ClassMetaData cmd = (ClassMetaData)getStack();
AbstractMemberMetaData mmd = newFieldObject(cmd, attrs);
// Tag this field as the version field
VersionMetaData vermd = new VersionMetaData(VersionStrategy.VERSION_NUMBER.toString(), mmd.getName());
cmd.setVersionMetaData(vermd);
pushStack(mmd);
}
}
else if (localName.equals("discriminator-value"))
{
// Processed in endElement()
}
else if (localName.equals("discriminator-column"))
{
ClassMetaData cmd = (ClassMetaData)getStack();
InheritanceMetaData inhmd = cmd.getInheritanceMetaData();
if (inhmd == null)
{
// Add an empty inheritance specification
inhmd = new InheritanceMetaData(cmd, null);
cmd.setInheritanceMetaData(inhmd);
}
DiscriminatorMetaData discmd = inhmd.getDiscriminatorMetaData();
if (discmd == null)
{
// User hasnt specified discriminator value so use "provider-specific function" (JPA spec 9.1.3.1) - what a joke spec
discmd = new DiscriminatorMetaData(inhmd, null, null, DiscriminatorStrategy.CLASS_NAME.toString(), "true");
inhmd.setDiscriminatorMetaData(discmd);
}
String jdbcType = null;
String discType = getAttr(attrs, "discriminator-type");
if (discType != null)
{
if (discType.equalsIgnoreCase("STRING"))
{
jdbcType = "VARCHAR";
}
else if (discType.equalsIgnoreCase("CHAR"))
{
jdbcType = "CHAR";
}
else if (discType.equalsIgnoreCase("INTEGER"))
{
jdbcType = "INTEGER";
}
}
ColumnMetaData colmd = new ColumnMetaData(discmd, getAttr(attrs, "name"), null, null, jdbcType, null, getAttr(attrs, "length"),
null, null, null, null, null, null, null);
discmd.setColumnMetaData(colmd);
}
else if (localName.equals("generated-value"))
{
// generated value for this field
AbstractMemberMetaData fmd = (AbstractMemberMetaData)getStack();
IdentityStrategy idStrategy = IdentityStrategy.getIdentityStrategy(getAttr(attrs, "strategy"));
fmd.setValueStrategy(idStrategy);
fmd.setValueGeneratorName(getAttr(attrs, "generator"));
}
else if (localName.equals("join-table"))
{
// Join table for this field
AbstractMemberMetaData fmd = (AbstractMemberMetaData)getStack();
JoinMetaData joinmd = new JoinMetaData(fmd, null, null, null, null, null, null, null, null);
String tableName = getAttr(attrs, "name");
String schemaName = getAttr(attrs, "schema");
String catalogName = getAttr(attrs, "catalog");
fmd.setJoinMetaData(joinmd);
if (!StringUtils.isWhitespace(tableName))
{
fmd.setTable(tableName);
}
if (!StringUtils.isWhitespace(schemaName))
{
fmd.setSchema(schemaName);
}
if (!StringUtils.isWhitespace(catalogName))
{
fmd.setSchema(catalogName);
}
pushStack(joinmd);
}
else if (localName.equals("column"))
{
// Column for the current field
AbstractMemberMetaData fmd = (AbstractMemberMetaData)getStack();
ColumnMetaData colmd =
new ColumnMetaData(fmd,
getAttr(attrs, "name"),
null, // No "target" in JPA
null, // No "target-field" in JPA
null, // No "jdbc-type" in JPA (use Lob, Basic, Temporal to decide this)
null, // No "sql-type" in JPA
getAttr(attrs, "length"), // Should use precision if floating point type
getAttr(attrs, "scale"),
getAttr(attrs, "nullable"),
null, // No default-value in JPA
null, // No insert-value in JPA
getAttr(attrs, "insertable"),
getAttr(attrs, "updatable"),
getAttr(attrs, "unique"));
fmd.addColumn(colmd);
String table = getAttr(attrs, "table");
if (!StringUtils.isWhitespace(table))
{
// Using secondary table
fmd.setTable(table);
}
}
else if (localName.equals("join-column"))
{
MetaData md = getStack();
if (md instanceof JoinMetaData)
{
JoinMetaData joinmd = (JoinMetaData)md;
ColumnMetaData colmd =
new ColumnMetaData(joinmd,
getAttr(attrs, "name"),
getAttr(attrs, "referenced-column-name"),
null,
null, // Copy referenced column
null, // Copy referenced column
null, // Copy referenced column
null, // Copy referenced column
getAttr(attrs, "nullable"),
null, // No default-value in JPA
null, // No insert-value in JPA
getAttr(attrs, "insertable"),
getAttr(attrs, "updatable"),
getAttr(attrs, "unique"));
joinmd.addColumn(colmd);
}
else if (md instanceof AbstractMemberMetaData)
{
// N-1, 1-1, 1-N (FK). Just set <column> for the field. Is this correct for 1-N FK ?
AbstractMemberMetaData fmd = (AbstractMemberMetaData)md;
ColumnMetaData colmd =
new ColumnMetaData(fmd,
getAttr(attrs, "name"),
getAttr(attrs, "referenced-column-name"),
null,
null, // Copy referenced column
null, // Copy referenced column
null, // Copy referenced column
null, // Copy referenced column
getAttr(attrs, "nullable"),
null, // No default-value in JPA
null, // No insert-value in JPA
getAttr(attrs, "insertable"),
getAttr(attrs, "updatable"),
getAttr(attrs, "unique"));
fmd.addColumn(colmd);
}
}
else if (localName.equals("inverse-join-column"))
{
MetaData md = getStack();
if (md instanceof JoinMetaData)
{
// Join table column that is FK to the element table
JoinMetaData joinmd = (JoinMetaData)md;
ElementMetaData elemmd = null;
AbstractMemberMetaData fmd = (AbstractMemberMetaData)joinmd.getParent();
if (fmd.getElementMetaData() != null)
{
elemmd = fmd.getElementMetaData();
}
else
{
elemmd = new ElementMetaData(fmd, null, null, null, null, null, null);
fmd.setElementMetaData(elemmd);
}
ColumnMetaData colmd =
new ColumnMetaData(elemmd,
getAttr(attrs, "name"),
getAttr(attrs, "referenced-column-name"),
null,
null, // Copy referenced column
null, // Copy referenced column
null, // Copy referenced column
null, // Copy referenced column
getAttr(attrs, "nullable"),
null, // No default-value in JPA
null, // No insert-value in JPA
getAttr(attrs, "insertable"),
getAttr(attrs, "updatable"),
getAttr(attrs, "unique"));
elemmd.addColumn(colmd);
}
}
else if (localName.equals("unique-constraint"))
{
MetaData md = getStack();
if (md instanceof AbstractClassMetaData)
{
// Unique constraint on primary table
AbstractClassMetaData cmd = (AbstractClassMetaData)md;
UniqueMetaData unimd = new UniqueMetaData(null, cmd.getTable(), null); // Columns are in subelement
cmd.addUniqueConstraint(unimd);
pushStack(unimd);
}
else if (md instanceof JoinMetaData)
{
// Unique constraint on secondary table or join table
JoinMetaData joinmd = (JoinMetaData)md;
UniqueMetaData unimd = new UniqueMetaData(null, null, null);
joinmd.setUniqueMetaData(unimd);
pushStack(unimd);
}
}
else if (localName.equals("entity-listeners"))
{
// Nothing to add at this point
}
else if (localName.equals("entity-listener"))
{
MetaData md = getStack();
EventListenerMetaData elmd = new EventListenerMetaData(getAttr(attrs, "class"));
if (md instanceof AbstractClassMetaData)
{
// Specified at <entity> or <mapped-superclass>
((AbstractClassMetaData)md).addListener(elmd);
}
else if (md instanceof FileMetaData)
{
// Specified at <persistence-unit-defaults>
((FileMetaData)md).addListener(elmd);
}
pushStack(elmd);
}
else if (localName.equals("pre-persist"))
{
// Pre-create callback
MetaData md = getStack();
if (md instanceof AbstractClassMetaData)
{
// Specified at <entity> or <mapped-superclass>
AbstractClassMetaData cmd = (AbstractClassMetaData)md;
EventListenerMetaData elmd = cmd.getListenerForClass(cmd.getFullClassName());
if (elmd == null)
{
elmd = new EventListenerMetaData(cmd.getFullClassName());
cmd.addListener(elmd);
}
elmd.addCallback("javax.persistence.PrePersist", getAttr(attrs, "method-name"));
}
else
{
// Specified at <entity-listener>
EventListenerMetaData elmd = (EventListenerMetaData)md;
elmd.addCallback("javax.persistence.PrePersist", getAttr(attrs, "method-name"));
}
}
else if (localName.equals("post-persist"))
{
// Post-create callback
MetaData md = getStack();
if (md instanceof AbstractClassMetaData)
{
// Specified at <entity> or <mapped-superclass>
AbstractClassMetaData cmd = (AbstractClassMetaData)md;
EventListenerMetaData elmd = cmd.getListenerForClass(cmd.getFullClassName());
if (elmd == null)
{
elmd = new EventListenerMetaData(cmd.getFullClassName());
cmd.addListener(elmd);
}
elmd.addCallback("javax.persistence.PostPersist", getAttr(attrs, "method-name"));
}
else
{
// Specified at <entity-listener>
EventListenerMetaData elmd = (EventListenerMetaData)md;
elmd.addCallback("javax.persistence.PostPersist", getAttr(attrs, "method-name"));
}
}
else if (localName.equals("pre-remove"))
{
// Pre-delete callback
MetaData md = getStack();
if (md instanceof AbstractClassMetaData)
{
// Specified at <entity> or <mapped-superclass>
AbstractClassMetaData cmd = (AbstractClassMetaData)md;
EventListenerMetaData elmd = cmd.getListenerForClass(cmd.getFullClassName());
if (elmd == null)
{
elmd = new EventListenerMetaData(cmd.getFullClassName());
cmd.addListener(elmd);
}
elmd.addCallback("javax.persistence.PreRemove", getAttr(attrs, "method-name"));
}
else
{
// Specified at <entity-listener>
EventListenerMetaData elmd = (EventListenerMetaData)md;
elmd.addCallback("javax.persistence.PreRemove", getAttr(attrs, "method-name"));
}
}
else if (localName.equals("post-remove"))
{
// Post-delete callback
MetaData md = getStack();
if (md instanceof AbstractClassMetaData)
{
// Specified at <entity> or <mapped-superclass>
AbstractClassMetaData cmd = (AbstractClassMetaData)md;
EventListenerMetaData elmd = cmd.getListenerForClass(cmd.getFullClassName());
if (elmd == null)
{
elmd = new EventListenerMetaData(cmd.getFullClassName());
cmd.addListener(elmd);
}
elmd.addCallback("javax.persistence.PostRemove", getAttr(attrs, "method-name"));
}
else
{
// Specified at <entity-listener>
EventListenerMetaData elmd = (EventListenerMetaData)md;
elmd.addCallback("javax.persistence.PostRemove", getAttr(attrs, "method-name"));
}
}
else if (localName.equals("pre-update"))
{
// Pre-store callback
MetaData md = getStack();
if (md instanceof AbstractClassMetaData)
{
// Specified at <entity> or <mapped-superclass>
AbstractClassMetaData cmd = (AbstractClassMetaData)md;
EventListenerMetaData elmd = cmd.getListenerForClass(cmd.getFullClassName());
if (elmd == null)
{
elmd = new EventListenerMetaData(cmd.getFullClassName());
cmd.addListener(elmd);
}
elmd.addCallback("javax.persistence.PreUpdate", getAttr(attrs, "method-name"));
}
else
{
// Specified at <entity-listener>
EventListenerMetaData elmd = (EventListenerMetaData)md;
elmd.addCallback("javax.persistence.PreUpdate", getAttr(attrs, "method-name"));
}
}
else if (localName.equals("post-update"))
{
// Post-store callback
MetaData md = getStack();
if (md instanceof AbstractClassMetaData)
{
// Specified at <entity> or <mapped-superclass>
AbstractClassMetaData cmd = (AbstractClassMetaData)md;
EventListenerMetaData elmd = cmd.getListenerForClass(cmd.getFullClassName());
if (elmd == null)
{
elmd = new EventListenerMetaData(cmd.getFullClassName());
cmd.addListener(elmd);
}
elmd.addCallback("javax.persistence.PostUpdate", getAttr(attrs, "method-name"));
}
else
{
// Specified at <entity-listener>
EventListenerMetaData elmd = (EventListenerMetaData)md;
elmd.addCallback("javax.persistence.PostUpdate", getAttr(attrs, "method-name"));
}
}
else if (localName.equals("post-load"))
{
// Post-load callback
MetaData md = getStack();
if (md instanceof AbstractClassMetaData)
{
// Specified at <entity> or <mapped-superclass>
AbstractClassMetaData cmd = (AbstractClassMetaData)md;
EventListenerMetaData elmd = cmd.getListenerForClass(cmd.getFullClassName());
if (elmd == null)
{
elmd = new EventListenerMetaData(cmd.getFullClassName());
cmd.addListener(elmd);
}
elmd.addCallback("javax.persistence.PostLoad", getAttr(attrs, "method-name"));
}
else
{
// Specified at <entity-listener>
EventListenerMetaData elmd = (EventListenerMetaData)md;
elmd.addCallback("javax.persistence.PostLoad", getAttr(attrs, "method-name"));
}
}
else if (localName.equals("attribute-override"))
{
// Override columns for a superclass field
AbstractClassMetaData cmd = (AbstractClassMetaData)getStack();
AbstractMemberMetaData fmd = newOverriddenFieldObject(cmd, attrs);
cmd.addMember(fmd);
pushStack(fmd);
}
else if (localName.equals("association-override"))
{
// Override columns for a superclass field
AbstractClassMetaData cmd = (AbstractClassMetaData)getStack();
AbstractMemberMetaData fmd = newOverriddenFieldObject(cmd, attrs);
cmd.addMember(fmd);
pushStack(fmd);
}
else if (localName.equals("exclude-default-listeners"))
{
AbstractClassMetaData cmd = (AbstractClassMetaData)getStack();
cmd.excludeDefaultListeners();
}
else if (localName.equals("exclude-superclass-listeners"))
{
AbstractClassMetaData cmd = (AbstractClassMetaData)getStack();
cmd.excludeSuperClassListeners();
}
else
{
String message = LOCALISER.msg("044037", qName);
JPOXLogger.METADATA.error(message);
throw new RuntimeException(message);
}
}
catch(RuntimeException ex)
{
JPOXLogger.METADATA.error(LOCALISER.msg("044042", qName, getStack(), uri), ex);
throw ex;
}
}
/**
* Handler method called at the end of an element.
* @param uri URI of the tag
* @param localName local name
* @param qName Name of element just ending
* @throws SAXException in parsing errors
*/
public void endElement(String uri, String localName, String qName)
throws SAXException
{
if (JPOXLogger.METADATA.isDebugEnabled())
{
JPOXLogger.METADATA.debug(LOCALISER.msg("044035", "<" + qName + ">", "" + stack.size()));
}
if (localName.length()<1)
{
localName = qName;
}
// Save the current string for elements that have a body value
String currentString = getString().trim();
if (currentString.length() > 0)
{
MetaData md = getStack();
if (localName.equals("schema"))
{
if (md instanceof FileMetaData)
{
// Specified at <entity-mappings> or <persistence-unit-defaults>
((FileMetaData)md).setSchema(currentString);
}
}
else if (localName.equals("catalog"))
{
if (md instanceof FileMetaData)
{
// Specified at <entity-mappings> or <persistence-unit-defaults>
((FileMetaData)md).setCatalog(currentString);
}
}
else if (localName.equals("access"))
{
if (md instanceof FileMetaData)
{
// Specified at <entity-mappings> or <persistence-unit-defaults>
if (currentString.equalsIgnoreCase("PROPERTY"))
{
// Use property access
propertyAccess = true;
}
}
}
else if (localName.equals("package"))
{
if (md instanceof FileMetaData)
{
// Add the default package
FileMetaData filemd = (FileMetaData)md;
filemd.addPackage(new PackageMetaData(filemd, currentString, null, null));
defaultPackageName = currentString;
}
}
else if (localName.equals("discriminator-value"))
{
if (md instanceof ClassMetaData)
{
// Add the discriminator value
ClassMetaData cmd = (ClassMetaData)md;
InheritanceMetaData inhmd = cmd.getInheritanceMetaData();
if (inhmd == null)
{
// Add an empty inheritance specification
inhmd = new InheritanceMetaData(cmd, null);
cmd.setInheritanceMetaData(inhmd);
}
String discrimValue = currentString;
DiscriminatorMetaData discmd = new DiscriminatorMetaData(inhmd, null, discrimValue,
DiscriminatorStrategy.VALUE_MAP.toString(), null);
inhmd.setDiscriminatorMetaData(discmd);
}
}
else if (localName.equals("column-name"))
{
if (md instanceof UniqueMetaData)
{
// Column for a unique constraint
((UniqueMetaData)md).addColumn(new ColumnMetaData(md, currentString));
}
}
else if (localName.equals("order-by"))
{
if (md instanceof AbstractMemberMetaData)
{
// "Ordered List" so add its ordering constraint
AbstractMemberMetaData fmd = (AbstractMemberMetaData)md;
fmd.setOrderMetaData(new OrderMetaData(currentString));
}
}
else if (localName.equals("query"))
{
if (md instanceof QueryMetaData)
{
// Named query, so set the query string
((QueryMetaData)md).setQuery(currentString);
}
}
else if (localName.equals("enumerated"))
{
if (md instanceof AbstractMemberMetaData)
{
AbstractMemberMetaData mmd = (AbstractMemberMetaData)md;
String enumerationType = currentString;
String jdbcType = "INTEGER";
if (enumerationType.equalsIgnoreCase("STRING"))
{
jdbcType = "VARCHAR";
}
if (mmd.getColumnMetaData() == null)
{
ColumnMetaData colmd = new ColumnMetaData(mmd, null, null, null, jdbcType,
null, null, null, null, null, null, null, null, null);
mmd.addColumn(colmd);
}
else
{
mmd.getColumnMetaData()[0].setJdbcType(jdbcType);
}
}
}
else if (localName.equals("temporal"))
{
if (md instanceof AbstractMemberMetaData)
{
AbstractMemberMetaData mmd = (AbstractMemberMetaData)md;
String enumerationType = currentString;
String jdbcType = null;
if (enumerationType.equalsIgnoreCase("DATE"))
{
jdbcType = "DATE";
}
else if (enumerationType.equalsIgnoreCase("TIME"))
{
jdbcType = "TIME";
}
else if (enumerationType.equalsIgnoreCase("TIMESTAMP"))
{
jdbcType = "TIMESTAMP";
}
if (mmd.getColumnMetaData() == null)
{
ColumnMetaData colmd = new ColumnMetaData(mmd, null, null, null, jdbcType,
null, null, null, null, null, null, null, null, null);
mmd.addColumn(colmd);
}
else
{
mmd.getColumnMetaData()[0].setJdbcType(jdbcType);
}
}
}
}
// Pop the tag
// If startElement pushes an element onto the stack need a remove here for that type
if (localName.equals("entity") ||
localName.equals("mapped-superclass") ||
localName.equals("entity-listener") ||
localName.equals("attribute-override") ||
localName.equals("association-override") ||
localName.equals("id") ||
localName.equals("embedded-id") ||
localName.equals("basic") ||
localName.equals("transient") ||
localName.equals("one-to-one") ||
localName.equals("one-to-many") ||
localName.equals("many-to-one") ||
localName.equals("many-to-many") ||
localName.equals("version") ||
localName.equals("secondary-table") ||
localName.equals("join-table") ||
localName.equals("unique-constraint") ||
localName.equals("named-query") ||
localName.equals("named-native-query") ||
localName.equals("sql-result-set-mapping"))
{
popStack();
}
}
}