blob: e467f0e010cba8309deb59c4e6f5acca3970944e [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you 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.
*/
package org.apache.openjpa.persistence;
import java.lang.reflect.Field;
import java.lang.reflect.Member;
import java.lang.reflect.Method;
import java.security.AccessController;
import java.security.PrivilegedActionException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.EnumSet;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
import java.util.Stack;
import javax.persistence.CascadeType;
import javax.persistence.GenerationType;
import static javax.persistence.CascadeType.*;
import org.apache.commons.lang.StringUtils;
import org.xml.sax.Attributes;
import org.xml.sax.Locator;
import org.xml.sax.SAXException;
import org.apache.openjpa.conf.OpenJPAConfiguration;
import org.apache.openjpa.enhance.PersistenceCapable;
import org.apache.openjpa.event.BeanLifecycleCallbacks;
import org.apache.openjpa.event.LifecycleCallbacks;
import org.apache.openjpa.event.LifecycleEvent;
import org.apache.openjpa.event.MethodLifecycleCallbacks;
import org.apache.openjpa.kernel.QueryLanguages;
import org.apache.openjpa.kernel.jpql.JPQLParser;
import org.apache.openjpa.lib.conf.Configurations;
import org.apache.openjpa.lib.log.Log;
import org.apache.openjpa.lib.meta.CFMetaDataParser;
import org.apache.openjpa.lib.util.J2DoPrivHelper;
import org.apache.openjpa.lib.util.Localizer;
import org.apache.openjpa.meta.ClassMetaData;
import org.apache.openjpa.meta.DelegatingMetaDataFactory;
import org.apache.openjpa.meta.FieldMetaData;
import org.apache.openjpa.meta.JavaTypes;
import org.apache.openjpa.meta.LifecycleMetaData;
import org.apache.openjpa.meta.MetaDataDefaults;
import org.apache.openjpa.meta.MetaDataFactory;
import static org.apache.openjpa.meta.MetaDataModes.*;
import org.apache.openjpa.meta.MetaDataRepository;
import org.apache.openjpa.meta.Order;
import org.apache.openjpa.meta.QueryMetaData;
import org.apache.openjpa.meta.SequenceMetaData;
import org.apache.openjpa.meta.ValueMetaData;
import static org.apache.openjpa.persistence.MetaDataTag.*;
import static org.apache.openjpa.persistence.PersistenceStrategy.*;
import org.apache.openjpa.util.ImplHelper;
import serp.util.Numbers;
/**
* Custom SAX parser used by the system to quickly parse persistence i
* metadata files.
*
* @author Steve Kim
* @nojavadoc
*/
public class XMLPersistenceMetaDataParser
extends CFMetaDataParser
implements PersistenceMetaDataFactory.Parser {
// parse constants
protected static final String ELEM_PKG = "package";
protected static final String ELEM_ACCESS = "access";
protected static final String ELEM_ATTRS = "attributes";
protected static final String ELEM_LISTENER = "entity-listener";
protected static final String ELEM_CASCADE = "cascade";
protected static final String ELEM_CASCADE_ALL = "cascade-all";
protected static final String ELEM_CASCADE_PER = "cascade-persist";
protected static final String ELEM_CASCADE_MER = "cascade-merge";
protected static final String ELEM_CASCADE_REM = "cascade-remove";
protected static final String ELEM_CASCADE_REF = "cascade-refresh";
protected static final String ELEM_PU_META = "persistence-unit-metadata";
protected static final String ELEM_PU_DEF = "persistence-unit-defaults";
protected static final String ELEM_XML_MAP_META_COMPLETE =
"xml-mapping-metadata-complete";
private static final Map<String, Object> _elems =
new HashMap<String, Object>();
static {
_elems.put(ELEM_PKG, ELEM_PKG);
_elems.put(ELEM_ACCESS, ELEM_ACCESS);
_elems.put(ELEM_ATTRS, ELEM_ATTRS);
_elems.put(ELEM_LISTENER, ELEM_LISTENER);
_elems.put(ELEM_CASCADE, ELEM_CASCADE);
_elems.put(ELEM_CASCADE_ALL, ELEM_CASCADE_ALL);
_elems.put(ELEM_CASCADE_PER, ELEM_CASCADE_PER);
_elems.put(ELEM_CASCADE_REM, ELEM_CASCADE_REM);
_elems.put(ELEM_CASCADE_MER, ELEM_CASCADE_MER);
_elems.put(ELEM_CASCADE_REF, ELEM_CASCADE_REF);
_elems.put(ELEM_PU_META, ELEM_PU_META);
_elems.put(ELEM_PU_DEF, ELEM_PU_DEF);
_elems.put(ELEM_XML_MAP_META_COMPLETE, ELEM_XML_MAP_META_COMPLETE);
_elems.put("entity-listeners", ENTITY_LISTENERS);
_elems.put("pre-persist", PRE_PERSIST);
_elems.put("post-persist", POST_PERSIST);
_elems.put("pre-remove", PRE_REMOVE);
_elems.put("post-remove", POST_REMOVE);
_elems.put("pre-update", PRE_UPDATE);
_elems.put("post-update", POST_UPDATE);
_elems.put("post-load", POST_LOAD);
_elems.put("exclude-default-listeners", EXCLUDE_DEFAULT_LISTENERS);
_elems.put("exclude-superclass-listeners",
EXCLUDE_SUPERCLASS_LISTENERS);
_elems.put("named-query", QUERY);
_elems.put("named-native-query", NATIVE_QUERY);
_elems.put("query-hint", QUERY_HINT);
_elems.put("query", QUERY_STRING);
_elems.put("flush-mode", FLUSH_MODE);
_elems.put("sequence-generator", SEQ_GENERATOR);
_elems.put("id", ID);
_elems.put("id-class", ID_CLASS);
_elems.put("embedded-id", EMBEDDED_ID);
_elems.put("version", VERSION);
_elems.put("generated-value", GENERATED_VALUE);
_elems.put("map-key", MAP_KEY);
_elems.put("order-by", ORDER_BY);
_elems.put("lob", LOB);
_elems.put("basic", BASIC);
_elems.put("many-to-one", MANY_ONE);
_elems.put("one-to-one", ONE_ONE);
_elems.put("embedded", EMBEDDED);
_elems.put("one-to-many", ONE_MANY);
_elems.put("many-to-many", MANY_MANY);
_elems.put("transient", TRANSIENT);
}
private static final Localizer _loc = Localizer.forPackage
(XMLPersistenceMetaDataParser.class);
private final OpenJPAConfiguration _conf;
private MetaDataRepository _repos = null;
private AnnotationPersistenceMetaDataParser _parser = null;
private ClassLoader _envLoader = null;
private int _mode = MODE_NONE;
private boolean _override = false;
private final Stack _elements = new Stack();
private final Stack _parents = new Stack();
private Class _cls = null;
private int _fieldPos = 0;
private int _clsPos = 0;
private int _access = ClassMetaData.ACCESS_UNKNOWN;
private PersistenceStrategy _strategy = null;
private Set<CascadeType> _cascades = null;
private Set<CascadeType> _pkgCascades = null;
private Class _listener = null;
private Collection<LifecycleCallbacks>[] _callbacks = null;
private int[] _highs = null;
/**
* Constructor; supply configuration.
*/
public XMLPersistenceMetaDataParser(OpenJPAConfiguration conf) {
_conf = conf;
setValidating(true);
setLog(conf.getLog(OpenJPAConfiguration.LOG_METADATA));
setParseComments(true);
setMode(MODE_META | MODE_MAPPING | MODE_QUERY);
setParseText(true);
}
/**
* Configuration supplied on construction.
*/
public OpenJPAConfiguration getConfiguration() {
return _conf;
}
/**
* The annotation parser. When class is discovered in an XML file,
* we first parse any annotations present, then override with the XML.
*/
public AnnotationPersistenceMetaDataParser getAnnotationParser() {
return _parser;
}
/**
* The annotation parser. When class is discovered in an XML file,
* we first parse any annotations present, then override with the XML.
*/
public void setAnnotationParser(AnnotationPersistenceMetaDataParser parser){
_parser = parser;
}
/**
* Returns the repository for this parser. If none has been set, creates
* a new repository and sets it.
*/
public MetaDataRepository getRepository() {
if (_repos == null) {
MetaDataRepository repos = _conf.newMetaDataRepositoryInstance();
MetaDataFactory mdf = repos.getMetaDataFactory();
if (mdf instanceof DelegatingMetaDataFactory)
mdf = ((DelegatingMetaDataFactory) mdf).getInnermostDelegate();
if (mdf instanceof PersistenceMetaDataFactory)
((PersistenceMetaDataFactory) mdf).setXMLParser(this);
_repos = repos;
}
return _repos;
}
/**
* Set the metadata repository for this parser.
*/
public void setRepository(MetaDataRepository repos) {
_repos = repos;
if (repos != null
&& (repos.getValidate() & repos.VALIDATE_RUNTIME) != 0)
setParseComments(false);
}
/**
* Return the environmental class loader to pass on to parsed
* metadata instances.
*/
public ClassLoader getEnvClassLoader() {
return _envLoader;
}
/**
* Set the environmental class loader to pass on to parsed
* metadata instances.
*/
public void setEnvClassLoader(ClassLoader loader) {
_envLoader = loader;
}
/**
* Whether to allow later parses of mapping information to override
* earlier information for the same class. Defaults to false. Useful
* when a tool is mapping a class, so that .jdo file partial mapping
* information can be used even when mappings are stored in .orm files
* or other locations.
*/
public boolean getMappingOverride() {
return _override;
}
/**
* Whether to allow later parses of mapping information to override
* earlier information for the same class. Defaults to false. Useful
* when a tool is mapping a class, so that .jdo file partial mapping
* information can be used even when mappings are stored in .orm files
* or other locations.
*/
public void setMappingOverride(boolean override) {
_override = override;
}
/**
* The parse mode according to the expected document type. The
* mode constants act as bit flags, and therefore can be combined.
*/
public int getMode() {
return _mode;
}
/**
* The parse mode according to the expected document type.
*/
public void setMode(int mode, boolean on) {
if (mode == MODE_NONE)
setMode(MODE_NONE);
else if (on)
setMode(_mode | mode);
else
setMode(_mode & ~mode);
}
/**
* The parse mode according to the expected document type.
*/
public void setMode(int mode) {
_mode = mode;
if (_parser != null)
_parser.setMode(mode);
}
/**
* Convenience method for interpreting {@link #getMode}.
*/
protected boolean isMetaDataMode() {
return (_mode & MODE_META) != 0;
}
/**
* Convenience method for interpreting {@link #getMode}.
*/
protected boolean isQueryMode() {
return (_mode & MODE_QUERY) != 0;
}
/**
* Convenience method for interpreting {@link #getMode}.
*/
protected boolean isMappingMode() {
return (_mode & MODE_MAPPING) != 0;
}
/**
* Returns true if we're in mapping mode or in metadata mode with
* mapping override enabled.
*/
protected boolean isMappingOverrideMode() {
return isMappingMode() || (_override && isMetaDataMode());
}
///////////////
// XML parsing
///////////////
/**
* Push a parse element onto the stack.
*/
protected void pushElement(Object elem) {
_elements.push(elem);
}
/**
* Pop a parse element from the stack.
*/
protected Object popElement() {
return _elements.pop();
}
/**
* Return the current element being parsed. May be a class metadata,
* field metadata, query metadata, etc.
*/
protected Object currentElement() {
if (_elements.isEmpty())
return null;
return _elements.peek();
}
/**
* Return the current {@link PersistenceStrategy} if any.
*/
protected PersistenceStrategy currentStrategy() {
return _strategy;
}
/**
* Return the tag of the current parent element.
*/
protected Object currentParent() {
if (_parents.isEmpty())
return null;
return _parents.peek();
}
/**
* Return whether we're running the parser at runtime.
*/
protected boolean isRuntime() {
return (getRepository().getValidate()
& MetaDataRepository.VALIDATE_RUNTIME) != 0;
}
@Override
protected Object getSchemaSource() {
return XMLPersistenceMetaDataParser.class.getResourceAsStream
("orm-xsd.rsrc");
}
@Override
protected String getPackageAttributeName() {
return null;
}
@Override
protected String getClassAttributeName() {
return "class";
}
@Override
protected int getClassElementDepth() {
return 1;
}
@Override
protected boolean isClassElementName(String name) {
return "entity".equals(name)
|| "embeddable".equals(name)
|| "mapped-superclass".equals(name);
}
@Override
protected void reset() {
super.reset();
_elements.clear();
_parents.clear();
_cls = null;
_fieldPos = 0;
_clsPos = 0;
_access = ClassMetaData.ACCESS_UNKNOWN;
_strategy = null;
_listener = null;
_callbacks = null;
_highs = null;
_cascades = null;
_pkgCascades = null;
}
@Override
protected boolean startSystemElement(String name, Attributes attrs)
throws SAXException {
Object tag = (Object) _elems.get(name);
boolean ret = false;
if (tag == null) {
if (isMappingOverrideMode())
tag = startSystemMappingElement(name, attrs);
ret = tag != null;
} else if (tag instanceof MetaDataTag) {
switch ((MetaDataTag) tag) {
case QUERY:
ret = startNamedQuery(attrs);
break;
case QUERY_HINT:
ret = startQueryHint(attrs);
break;
case NATIVE_QUERY:
ret = startNamedNativeQuery(attrs);
break;
case QUERY_STRING:
ret = startQueryString(attrs);
break;
case SEQ_GENERATOR:
ret = startSequenceGenerator(attrs);
break;
case FLUSH_MODE:
ret = startFlushMode(attrs);
break;
case ENTITY_LISTENERS:
ret = startEntityListeners(attrs);
break;
case PRE_PERSIST:
case POST_PERSIST:
case PRE_REMOVE:
case POST_REMOVE:
case PRE_UPDATE:
case POST_UPDATE:
case POST_LOAD:
ret = startCallback((MetaDataTag) tag, attrs);
break;
default:
warnUnsupportedTag(name);
}
} else if (tag == ELEM_PU_META || tag == ELEM_PU_DEF)
ret = isMetaDataMode();
else if (tag == ELEM_XML_MAP_META_COMPLETE)
setAnnotationParser(null);
else if (tag == ELEM_ACCESS)
ret = _mode != MODE_QUERY;
else if (tag == ELEM_LISTENER)
ret = startEntityListener(attrs);
else if (tag == ELEM_CASCADE)
ret = isMetaDataMode();
else if (tag == ELEM_CASCADE_ALL || tag == ELEM_CASCADE_PER
|| tag == ELEM_CASCADE_MER || tag == ELEM_CASCADE_REM
|| tag == ELEM_CASCADE_REF)
ret = startCascade(tag, attrs);
if (ret)
_parents.push(tag);
return ret;
}
@Override
protected void endSystemElement(String name)
throws SAXException {
Object tag = _elems.get(name);
if (tag == null && isMappingOverrideMode())
endSystemMappingElement(name);
else if (tag instanceof MetaDataTag) {
switch ((MetaDataTag) tag) {
case QUERY:
endNamedQuery();
break;
case QUERY_HINT:
endQueryHint();
break;
case NATIVE_QUERY:
endNamedNativeQuery();
break;
case QUERY_STRING:
endQueryString();
break;
case SEQ_GENERATOR:
endSequenceGenerator();
break;
}
} else if (tag == ELEM_ACCESS)
endAccess();
else if (tag == ELEM_LISTENER)
endEntityListener();
_parents.pop();
}
/**
* Implement to parse a mapping element outside of any class.
*
* @return the tag for the given element, or null to skip the element
*/
protected Object startSystemMappingElement(String name, Attributes attrs)
throws SAXException {
return null;
}
/**
* Implement to parse a mapping element outside of any class.
*/
protected void endSystemMappingElement(String name)
throws SAXException {
}
@Override
protected boolean startClassElement(String name, Attributes attrs)
throws SAXException {
Object tag = (Object) _elems.get(name);
boolean ret = false;
if (tag == null) {
if (isMappingOverrideMode())
tag = startClassMappingElement(name, attrs);
ret = tag != null;
} else if (tag instanceof MetaDataTag) {
switch ((MetaDataTag) tag) {
case GENERATED_VALUE:
ret = startGeneratedValue(attrs);
break;
case ID:
ret = startId(attrs);
break;
case EMBEDDED_ID:
ret = startEmbeddedId(attrs);
break;
case ID_CLASS:
ret = startIdClass(attrs);
break;
case LOB:
ret = startLob(attrs);
break;
case QUERY:
ret = startNamedQuery(attrs);
break;
case QUERY_HINT:
ret = startQueryHint(attrs);
break;
case NATIVE_QUERY:
ret = startNamedNativeQuery(attrs);
break;
case QUERY_STRING:
ret = startQueryString(attrs);
break;
case SEQ_GENERATOR:
ret = startSequenceGenerator(attrs);
break;
case VERSION:
ret = startVersion(attrs);
break;
case MAP_KEY:
ret = startMapKey(attrs);
break;
case FLUSH_MODE:
ret = startFlushMode(attrs);
break;
case ORDER_BY:
case ENTITY_LISTENERS:
ret = isMetaDataMode();
break;
case EXCLUDE_DEFAULT_LISTENERS:
ret = startExcludeDefaultListeners(attrs);
break;
case EXCLUDE_SUPERCLASS_LISTENERS:
ret = startExcludeSuperclassListeners(attrs);
break;
case PRE_PERSIST:
case POST_PERSIST:
case PRE_REMOVE:
case POST_REMOVE:
case PRE_UPDATE:
case POST_UPDATE:
case POST_LOAD:
ret = startCallback((MetaDataTag) tag, attrs);
break;
default:
warnUnsupportedTag(name);
}
} else if (tag instanceof PersistenceStrategy) {
PersistenceStrategy ps = (PersistenceStrategy) tag;
ret = startStrategy(ps, attrs);
if (ret)
_strategy = ps;
} else if (tag == ELEM_LISTENER)
ret = startEntityListener(attrs);
else if (tag == ELEM_ATTRS)
ret = _mode != MODE_QUERY;
else if (tag == ELEM_CASCADE)
ret = isMetaDataMode();
else if (tag == ELEM_CASCADE_ALL || tag == ELEM_CASCADE_PER
|| tag == ELEM_CASCADE_MER || tag == ELEM_CASCADE_REM
|| tag == ELEM_CASCADE_REF)
ret = startCascade(tag, attrs);
if (ret)
_parents.push(tag);
return ret;
}
@Override
protected void endClassElement(String name)
throws SAXException {
Object tag = _elems.get(name);
if (tag == null && isMappingOverrideMode())
endClassMappingElement(name);
else if (tag instanceof MetaDataTag) {
switch ((MetaDataTag) tag) {
case GENERATED_VALUE:
endGeneratedValue();
break;
case ID:
endId();
break;
case EMBEDDED_ID:
endEmbeddedId();
break;
case ID_CLASS:
endIdClass();
break;
case LOB:
endLob();
break;
case QUERY:
endNamedQuery();
break;
case QUERY_HINT:
endQueryHint();
break;
case NATIVE_QUERY:
endNamedNativeQuery();
break;
case QUERY_STRING:
endQueryString();
break;
case SEQ_GENERATOR:
endSequenceGenerator();
break;
case VERSION:
endVersion();
break;
case ORDER_BY:
endOrderBy();
break;
}
} else if (tag instanceof PersistenceStrategy)
endStrategy((PersistenceStrategy) tag);
else if (tag == ELEM_ACCESS)
endAccess();
else if (tag == ELEM_LISTENER)
endEntityListener();
_parents.pop();
}
/**
* Log warning about an unsupported tag.
*/
private void warnUnsupportedTag(String name) {
Log log = getLog();
if (log.isInfoEnabled())
log.trace(_loc.get("unsupported-tag", name));
}
/**
* Implement to parse a mapping element within a class.
*
* @return the tag for the given element, or null to skip element
*/
protected Object startClassMappingElement(String name, Attributes attrs)
throws SAXException {
return null;
}
/**
* Implement to parse a mapping element within a class.
*/
protected void endClassMappingElement(String name)
throws SAXException {
}
@Override
protected boolean startClass(String elem, Attributes attrs)
throws SAXException {
super.startClass(elem, attrs);
// query mode only?
_cls = classForName(currentClassName());
if (_mode == MODE_QUERY) {
if (_parser != null &&
!"true".equals(attrs.getValue("metadata-complete")))
_parser.parse(_cls);
return true;
}
Log log = getLog();
if (log.isTraceEnabled())
log.trace(_loc.get("parse-class", _cls.getName()));
MetaDataRepository repos = getRepository();
ClassMetaData meta = repos.getCachedMetaData(_cls);
if (meta != null
&& ((isMetaDataMode() && (meta.getSourceMode() & MODE_META) != 0)
|| (isMappingMode() && (meta.getSourceMode() & MODE_MAPPING) != 0)))
{
if (log.isWarnEnabled())
log.warn(_loc.get("dup-metadata", _cls, getSourceName()));
_cls = null;
return false;
}
// if we don't know the access type, check to see if a superclass
// has already defined the access type
int defaultAccess = _access;
if (defaultAccess == ClassMetaData.ACCESS_UNKNOWN) {
ClassMetaData sup = repos.getCachedMetaData(_cls.getSuperclass());
if (sup != null)
defaultAccess = sup.getAccessType();
}
if (meta == null) {
// add metadata for this type
int access = toAccessType(attrs.getValue("access"), defaultAccess);
meta = repos.addMetaData(_cls, access);
meta.setEnvClassLoader(_envLoader);
meta.setSourceMode(MODE_NONE);
// parse annotations first so XML overrides them
if (_parser != null &&
!"true".equals(attrs.getValue("metadata-complete")))
_parser.parse(_cls);
}
boolean mappedSuper = "mapped-superclass".equals(elem);
if (isMetaDataMode()) {
meta.setSource(getSourceFile(), meta.SRC_XML);
meta.setSourceMode(MODE_META, true);
Locator locator = getLocation().getLocator();
if (locator != null) {
meta.setLineNumber(Numbers.valueOf(locator.getLineNumber()));
meta.setColNumber(Numbers.valueOf(locator.getColumnNumber()));
}
meta.setListingIndex(_clsPos);
String name = attrs.getValue("name");
if (!StringUtils.isEmpty(name))
meta.setTypeAlias(name);
meta.setAbstract(mappedSuper);
meta.setEmbeddedOnly(mappedSuper || "embeddable".equals(elem));
if (mappedSuper)
meta.setIdentityType(meta.ID_UNKNOWN);
}
if (isMappingMode())
meta.setSourceMode(MODE_MAPPING, true);
if (isMappingOverrideMode())
startClassMapping(meta, mappedSuper, attrs);
if (isQueryMode())
meta.setSourceMode(MODE_QUERY, true);
_clsPos++;
_fieldPos = 0;
addComments(meta);
pushElement(meta);
return true;
}
@Override
protected void endClass(String elem)
throws SAXException {
if (_mode != MODE_QUERY) {
ClassMetaData meta = (ClassMetaData) popElement();
storeCallbacks(meta);
if (isMappingOverrideMode())
endClassMapping(meta);
}
_cls = null;
super.endClass(elem);
}
/**
* Implement to add mapping attributes to class.
*/
protected void startClassMapping(ClassMetaData mapping,
boolean mappedSuper, Attributes attrs)
throws SAXException {
}
/**
* Implement to finalize class mapping.
*/
protected void endClassMapping(ClassMetaData mapping)
throws SAXException {
}
/**
* Default access element.
*/
private void endAccess() {
_access = toAccessType(currentText(), ClassMetaData.ACCESS_UNKNOWN);
}
/**
* Parse the given string as an entity access type, defaulting to given
* default if string is empty.
*/
private int toAccessType(String str, int def) {
if (StringUtils.isEmpty(str))
return def;
if ("PROPERTY".equals(str))
return ClassMetaData.ACCESS_PROPERTY;
return ClassMetaData.ACCESS_FIELD;
}
/**
* Parse flush-mode element.
*/
private boolean startFlushMode(Attributes attrs)
throws SAXException {
Log log = getLog();
if (log.isWarnEnabled())
log.warn(_loc.get("unsupported", "flush-mode", getSourceName()));
return false;
}
/**
* Parse sequence-generator.
*/
protected boolean startSequenceGenerator(Attributes attrs) {
if (!isMappingOverrideMode())
return false;
String name = attrs.getValue("name");
Log log = getLog();
if (log.isTraceEnabled())
log.trace(_loc.get("parse-sequence", name));
SequenceMetaData meta = getRepository().getCachedSequenceMetaData(name);
if (meta != null && log.isWarnEnabled())
log.warn(_loc.get("override-sequence", name));
meta = getRepository().addSequenceMetaData(name);
String seq = attrs.getValue("sequence-name");
String val = attrs.getValue("initial-value");
int initial = val == null ? 1 : Integer.parseInt(val);
val = attrs.getValue("allocation-size");
int allocate = val == null ? 50 : Integer.parseInt(val);
String clsName, props;
if (seq == null || seq.indexOf('(') == -1) {
clsName = SequenceMetaData.IMPL_NATIVE;
props = null;
} else { // plugin
clsName = Configurations.getClassName(seq);
props = Configurations.getProperties(seq);
seq = null;
}
meta.setSequencePlugin(Configurations.getPlugin(clsName, props));
meta.setSequence(seq);
meta.setInitialValue(initial);
meta.setAllocate(allocate);
Object cur = currentElement();
Object scope = (cur instanceof ClassMetaData)
? ((ClassMetaData) cur).getDescribedType() : null;
meta.setSource(getSourceFile(), scope, meta.SRC_XML);
Locator locator = getLocation().getLocator();
if (locator != null) {
meta.setLineNumber(Numbers.valueOf(locator.getLineNumber()));
meta.setColNumber(Numbers.valueOf(locator.getColumnNumber()));
}
return true;
}
protected void endSequenceGenerator() {
}
/**
* Parse id.
*/
protected boolean startId(Attributes attrs)
throws SAXException {
FieldMetaData fmd = parseField(attrs);
fmd.setExplicit(true);
fmd.setPrimaryKey(true);
return true;
}
protected void endId()
throws SAXException {
finishField();
}
/**
* Parse embedded-id.
*/
protected boolean startEmbeddedId(Attributes attrs)
throws SAXException {
FieldMetaData fmd = parseField(attrs);
fmd.setExplicit(true);
fmd.setPrimaryKey(true);
fmd.setEmbedded(true);
if (fmd.getEmbeddedMetaData() == null)
fmd.addEmbeddedMetaData();
return true;
}
protected void endEmbeddedId()
throws SAXException {
finishField();
}
/**
* Parse id-class.
*/
protected boolean startIdClass(Attributes attrs)
throws SAXException {
if (!isMetaDataMode())
return false;
ClassMetaData meta = (ClassMetaData) currentElement();
String cls = attrs.getValue("class");
Class idCls = null;
try {
idCls = classForName(cls);
} catch (Throwable t) {
throw getException(_loc.get("invalid-id-class", meta, cls), t);
}
meta.setObjectIdType(idCls, true);
return true;
}
protected void endIdClass()
throws SAXException {
}
/**
* Parse lob.
*/
protected boolean startLob(Attributes attrs)
throws SAXException {
FieldMetaData fmd = (FieldMetaData) currentElement();
if (fmd.getDeclaredTypeCode() != JavaTypes.STRING
&& fmd.getDeclaredType() != char[].class
&& fmd.getDeclaredType() != Character[].class
&& fmd.getDeclaredType() != byte[].class
&& fmd.getDeclaredType() != Byte[].class)
fmd.setSerialized(true);
return true;
}
protected void endLob()
throws SAXException {
}
/**
* Parse generated-value.
*/
protected boolean startGeneratedValue(Attributes attrs)
throws SAXException {
if (!isMappingOverrideMode())
return false;
String strategy = attrs.getValue("strategy");
String generator = attrs.getValue("generator");
GenerationType type = StringUtils.isEmpty(strategy)
? GenerationType.AUTO : GenerationType.valueOf(strategy);
FieldMetaData fmd = (FieldMetaData) currentElement();
AnnotationPersistenceMetaDataParser.parseGeneratedValue(fmd, type,
generator);
return true;
}
protected void endGeneratedValue()
throws SAXException {
}
/**
* Lazily parse cascades.
*/
protected boolean startCascade(Object tag, Attributes attrs)
throws SAXException {
if (!isMetaDataMode())
return false;
Set<CascadeType> cascades = null;
if (currentElement() instanceof FieldMetaData) {
if (_cascades == null)
_cascades = EnumSet.noneOf(CascadeType.class);
cascades = _cascades;
} else {
if (_pkgCascades == null)
_pkgCascades = EnumSet.noneOf(CascadeType.class);
cascades = _pkgCascades;
}
boolean all = ELEM_CASCADE_ALL == tag;
if (all || ELEM_CASCADE_PER == tag)
cascades.add(PERSIST);
if (all || ELEM_CASCADE_REM == tag)
cascades.add(REMOVE);
if (all || ELEM_CASCADE_MER == tag)
cascades.add(MERGE);
if (all || ELEM_CASCADE_REF == tag)
cascades.add(REFRESH);
return true;
}
/**
* Set the cached cascades into the field.
*/
protected void setCascades(FieldMetaData fmd) {
Set<CascadeType> cascades = _cascades;
if (_cascades == null)
cascades = _pkgCascades;
if (cascades == null)
return;
ValueMetaData vmd = fmd;
switch (_strategy) {
case ONE_MANY:
case MANY_MANY:
vmd = fmd.getElement();
}
for (CascadeType cascade : cascades) {
switch (cascade) {
case PERSIST:
vmd.setCascadePersist(ValueMetaData.CASCADE_IMMEDIATE);
break;
case MERGE:
vmd.setCascadeAttach(ValueMetaData.CASCADE_IMMEDIATE);
break;
case REMOVE:
vmd.setCascadeDelete(ValueMetaData.CASCADE_IMMEDIATE);
break;
case REFRESH:
vmd.setCascadeRefresh(ValueMetaData.CASCADE_IMMEDIATE);
break;
}
}
_cascades = null;
}
/**
* Parse common field attributes.
*/
private FieldMetaData parseField(Attributes attrs)
throws SAXException {
ClassMetaData meta = (ClassMetaData) currentElement();
String name = attrs.getValue("name");
FieldMetaData field = meta.getDeclaredField(name);
if ((field == null || field.getDeclaredType() == Object.class)
&& meta.getDescribedType() != Object.class) {
Member member = null;
Class type = null;
int def = _repos.getMetaDataFactory().getDefaults().
getDefaultAccessType();
try {
if (meta.getAccessType() == ClassMetaData.ACCESS_PROPERTY
|| (meta.getAccessType() == ClassMetaData.ACCESS_UNKNOWN
&& def == ClassMetaData.ACCESS_PROPERTY)) {
String cap = StringUtils.capitalize(name);
type = meta.getDescribedType();
try {
member = (Method) AccessController.doPrivileged(
J2DoPrivHelper.getDeclaredMethodAction(
type, "get" + cap,
(Class[]) null));// varargs disambiguate
} catch (Exception excep) {
try {
member = (Method) AccessController.doPrivileged(
J2DoPrivHelper.getDeclaredMethodAction(
type, "is" + cap, (Class[]) null));
} catch (Exception excep2) {
throw excep;
}
}
type = ((Method) member).getReturnType();
} else {
member = (Field) AccessController.doPrivileged(
J2DoPrivHelper.getDeclaredFieldAction(
meta.getDescribedType(), name));
type = ((Field) member).getType();
}
} catch (Exception e) {
if (e instanceof PrivilegedActionException)
e = ((PrivilegedActionException) e).getException();
throw getException(_loc.get("invalid-attr", name, meta), e);
}
if (field == null) {
field = meta.addDeclaredField(name, type);
PersistenceMetaDataDefaults.setCascadeNone(field);
PersistenceMetaDataDefaults.setCascadeNone(field.getKey());
PersistenceMetaDataDefaults.setCascadeNone(field.getElement());
}
field.backingMember(member);
} else if (field == null) {
field = meta.addDeclaredField(name, Object.class);
PersistenceMetaDataDefaults.setCascadeNone(field);
PersistenceMetaDataDefaults.setCascadeNone(field.getKey());
PersistenceMetaDataDefaults.setCascadeNone(field.getElement());
}
if (isMetaDataMode())
field.setListingIndex(_fieldPos);
_fieldPos++;
pushElement(field);
addComments(field);
if (isMappingOverrideMode())
startFieldMapping(field, attrs);
return field;
}
/**
* Pops field element.
*/
private void finishField()
throws SAXException {
FieldMetaData field = (FieldMetaData) popElement();
setCascades(field);
if (isMappingOverrideMode())
endFieldMapping(field);
_strategy = null;
}
/**
* Implement to add field mapping data. Does nothing by default.
*/
protected void startFieldMapping(FieldMetaData field, Attributes attrs)
throws SAXException {
}
/**
* Implement to finalize field mapping. Does nothing by default.
*/
protected void endFieldMapping(FieldMetaData field)
throws SAXException {
}
/**
* Parse version.
*/
protected boolean startVersion(Attributes attrs)
throws SAXException {
FieldMetaData fmd = parseField(attrs);
fmd.setExplicit(true);
fmd.setVersion(true);
return true;
}
protected void endVersion()
throws SAXException {
finishField();
}
/**
* Parse strategy element.
*/
private boolean startStrategy(PersistenceStrategy strategy,
Attributes attrs)
throws SAXException {
FieldMetaData fmd = parseField(attrs);
fmd.setExplicit(true);
fmd.setManagement(FieldMetaData.MANAGE_PERSISTENT);
String val = attrs.getValue("fetch");
if (val != null)
fmd.setInDefaultFetchGroup("EAGER".equals(val));
val = attrs.getValue("optional");
if ("false".equals(val))
fmd.setNullValue(FieldMetaData.NULL_EXCEPTION);
else if ("true".equals(val)
&& fmd.getNullValue() == FieldMetaData.NULL_EXCEPTION) {
// Reset value if the field was annotated with optional=false.
// Otherwise leave it alone.
fmd.setNullValue(FieldMetaData.NULL_UNSET);
}
if (isMappingOverrideMode()) {
val = attrs.getValue("mapped-by");
if (val != null)
fmd.setMappedBy(val);
}
parseStrategy(fmd, strategy, attrs);
return true;
}
private void endStrategy(PersistenceStrategy strategy)
throws SAXException {
finishField();
}
/**
* Parse strategy specific attributes.
*/
private void parseStrategy(FieldMetaData fmd,
PersistenceStrategy strategy, Attributes attrs)
throws SAXException {
switch (strategy) {
case BASIC:
parseBasic(fmd, attrs);
break;
case EMBEDDED:
parseEmbedded(fmd, attrs);
break;
case ONE_ONE:
parseOneToOne(fmd, attrs);
break;
case MANY_ONE:
parseManyToOne(fmd, attrs);
break;
case MANY_MANY:
parseManyToMany(fmd, attrs);
break;
case ONE_MANY:
parseOneToMany(fmd, attrs);
break;
case TRANSIENT:
fmd.setManagement(FieldMetaData.MANAGE_NONE);
break;
}
}
/**
* Parse basic.
*/
protected void parseBasic(FieldMetaData fmd, Attributes attrs)
throws SAXException {
}
/**
* Parse embedded.
*/
protected void parseEmbedded(FieldMetaData fmd, Attributes attrs)
throws SAXException {
assertPC(fmd, "Embedded");
fmd.setEmbedded(true);
fmd.setSerialized(false); // override any Lob annotation
if (fmd.getEmbeddedMetaData() == null)
fmd.addEmbeddedMetaData();
}
/**
* Throw proper exception if given value is not possibly persistence
* capable.
*/
private void assertPC(FieldMetaData fmd, String attr)
throws SAXException {
if (!JavaTypes.maybePC(fmd))
throw getException(_loc.get("bad-meta-anno", fmd, attr));
}
/**
* Parse one-to-one.
*/
protected void parseOneToOne(FieldMetaData fmd, Attributes attrs)
throws SAXException {
String val = attrs.getValue("target-entity");
if (val != null)
fmd.setTypeOverride(classForName(val));
assertPC(fmd, "OneToOne");
fmd.setSerialized(false); // override any Lob annotation
if (!fmd.isDefaultFetchGroupExplicit())
fmd.setInDefaultFetchGroup(true);
}
/**
* Parse many-to-one.
*/
protected void parseManyToOne(FieldMetaData fmd, Attributes attrs)
throws SAXException {
String val = attrs.getValue("target-entity");
if (val != null)
fmd.setTypeOverride(classForName(val));
assertPC(fmd, "ManyToOne");
fmd.setSerialized(false); // override any Lob annotation
if (!fmd.isDefaultFetchGroupExplicit())
fmd.setInDefaultFetchGroup(true);
}
/**
* Parse many-to-many.
*/
protected void parseManyToMany(FieldMetaData fmd, Attributes attrs)
throws SAXException {
String val = attrs.getValue("target-entity");
if (val != null)
fmd.getElement().setDeclaredType(classForName(val));
assertPCCollection(fmd, "ManyToMany");
fmd.setSerialized(false); // override Lob in annotation
}
/**
* Throw exception if given field not a collection of possible persistence
* capables.
*/
private void assertPCCollection(FieldMetaData fmd, String attr)
throws SAXException {
switch (fmd.getDeclaredTypeCode()) {
case JavaTypes.ARRAY:
case JavaTypes.COLLECTION:
case JavaTypes.MAP:
if (JavaTypes.maybePC(fmd.getElement()))
break;
// no break
default:
throw getException(_loc.get("bad-meta-anno", fmd, attr));
}
}
/**
* Parse one-to-many.
*/
protected void parseOneToMany(FieldMetaData fmd, Attributes attrs)
throws SAXException {
String val = attrs.getValue("target-entity");
if (val != null)
fmd.getElement().setDeclaredType(classForName(val));
assertPCCollection(fmd, "OneToMany");
fmd.setSerialized(false); // override any Lob annotation
}
/**
* Parse map-key.
*/
private boolean startMapKey(Attributes attrs)
throws SAXException {
if (!isMappingOverrideMode())
return false;
FieldMetaData fmd = (FieldMetaData) currentElement();
String mapKey = attrs.getValue("name");
if (mapKey == null)
fmd.getKey().setValueMappedBy(ValueMetaData.MAPPED_BY_PK);
else
fmd.getKey().setValueMappedBy(mapKey);
return true;
}
/**
* Parse order-by.
*/
private void endOrderBy()
throws SAXException {
FieldMetaData fmd = (FieldMetaData) currentElement();
String dec = currentText();
if (StringUtils.isEmpty(dec))
dec = Order.ELEMENT + " asc";
fmd.setOrderDeclaration(dec);
}
/**
* Parse named-query.
*/
protected boolean startNamedQuery(Attributes attrs)
throws SAXException {
if (!isQueryMode())
return false;
String name = attrs.getValue("name");
Log log = getLog();
if (log.isTraceEnabled())
log.trace(_loc.get("parse-query", name));
QueryMetaData meta = getRepository().getCachedQueryMetaData(null, name);
if (meta != null && log.isWarnEnabled())
log.warn(_loc.get("override-query", name, currentLocation()));
meta = getRepository().addQueryMetaData(null, name);
meta.setDefiningType(_cls);
meta.setQueryString(attrs.getValue("query"));
meta.setLanguage(JPQLParser.LANG_JPQL);
Locator locator = getLocation().getLocator();
if (locator != null) {
meta.setLineNumber(Numbers.valueOf(locator.getLineNumber()));
meta.setColNumber(Numbers.valueOf(locator.getColumnNumber()));
}
Object cur = currentElement();
Object scope = (cur instanceof ClassMetaData)
? ((ClassMetaData) cur).getDescribedType() : null;
meta.setSource(getSourceFile(), scope, meta.SRC_XML);
if (isMetaDataMode())
meta.setSourceMode(MODE_META);
else if (isMappingMode())
meta.setSourceMode(MODE_MAPPING);
else
meta.setSourceMode(MODE_QUERY);
pushElement(meta);
return true;
}
protected void endNamedQuery()
throws SAXException {
popElement();
}
protected boolean startQueryString(Attributes attrs)
throws SAXException {
return true;
}
protected void endQueryString()
throws SAXException {
QueryMetaData meta = (QueryMetaData) currentElement();
meta.setQueryString(currentText());
}
/**
* Parse query-hint.
*/
protected boolean startQueryHint(Attributes attrs)
throws SAXException {
QueryMetaData meta = (QueryMetaData) currentElement();
meta.addHint(attrs.getValue("name"), attrs.getValue("value"));
return true;
}
protected void endQueryHint()
throws SAXException {
}
/**
* Parse native-named-query.
*/
protected boolean startNamedNativeQuery(Attributes attrs)
throws SAXException {
if (!isQueryMode())
return false;
String name = attrs.getValue("name");
Log log = getLog();
if (log.isTraceEnabled())
log.trace(_loc.get("parse-native-query", name));
QueryMetaData meta = getRepository().getCachedQueryMetaData(null, name);
if (meta != null && log.isWarnEnabled())
log.warn(_loc.get("override-query", name, currentLocation()));
meta = getRepository().addQueryMetaData(null, name);
meta.setDefiningType(_cls);
meta.setQueryString(attrs.getValue("query"));
meta.setLanguage(QueryLanguages.LANG_SQL);
String val = attrs.getValue("result-class");
if (val != null) {
Class type = classForName(val);
if (ImplHelper.isManagedType(getConfiguration(), type))
meta.setCandidateType(type);
else
meta.setResultType(type);
}
val = attrs.getValue("result-set-mapping");
if (val != null)
meta.setResultSetMappingName(val);
Object cur = currentElement();
Object scope = (cur instanceof ClassMetaData)
? ((ClassMetaData) cur).getDescribedType() : null;
meta.setSource(getSourceFile(), scope, meta.SRC_XML);
Locator locator = getLocation().getLocator();
if (locator != null) {
meta.setLineNumber(Numbers.valueOf(locator.getLineNumber()));
meta.setColNumber(Numbers.valueOf(locator.getColumnNumber()));
}
if (isMetaDataMode())
meta.setSourceMode(MODE_META);
else if (isMappingMode())
meta.setSourceMode(MODE_MAPPING);
else
meta.setSourceMode(MODE_QUERY);
pushElement(meta);
return true;
}
protected void endNamedNativeQuery()
throws SAXException {
popElement();
}
/**
* Start entity-listeners
*/
private boolean startEntityListeners(Attributes attrs)
throws SAXException {
if (!isMetaDataMode())
return false;
if (currentElement() == null)
return true;
// reset listeners declared in annotations.
LifecycleMetaData meta = ((ClassMetaData) currentElement()).
getLifecycleMetaData();
for (int i = 0; i < LifecycleEvent.ALL_EVENTS.length; i++)
meta.setDeclaredCallbacks(i, null, 0);
return true;
}
/**
* Parse exclude-default-listeners.
*/
private boolean startExcludeDefaultListeners(Attributes attrs)
throws SAXException {
if (!isMetaDataMode())
return false;
ClassMetaData meta = (ClassMetaData) currentElement();
meta.getLifecycleMetaData().setIgnoreSystemListeners(true);
return true;
}
/**
* Parse exclude-superclass-listeners.
*/
private boolean startExcludeSuperclassListeners(Attributes attrs)
throws SAXException {
if (!isMetaDataMode())
return false;
ClassMetaData meta = (ClassMetaData) currentElement();
meta.getLifecycleMetaData().setIgnoreSuperclassCallbacks
(LifecycleMetaData.IGNORE_HIGH);
return true;
}
/**
* Parse entity-listener.
*/
private boolean startEntityListener(Attributes attrs)
throws SAXException {
_listener = classForName(attrs.getValue("class"));
boolean system = currentElement() == null;
Collection<LifecycleCallbacks>[] parsed =
AnnotationPersistenceMetaDataParser.parseCallbackMethods(_listener,
null, true, true, _repos);
if (parsed == null)
return true;
if (_callbacks == null) {
_callbacks = (Collection<LifecycleCallbacks>[])
new Collection[LifecycleEvent.ALL_EVENTS.length];
if (!system)
_highs = new int[LifecycleEvent.ALL_EVENTS.length];
}
for (int i = 0; i < parsed.length; i++) {
if (parsed[i] == null)
continue;
if (_callbacks[i] == null)
_callbacks[i] = parsed[i];
else
_callbacks[i].addAll(parsed[i]);
if (!system)
_highs[i] += parsed[i].size();
}
return true;
}
private void endEntityListener()
throws SAXException {
// should be in endEntityListeners I think to merge callbacks
// into a single listener. But then the user cannot remove.
if (currentElement() == null && _callbacks != null) {
_repos.addSystemListener(new PersistenceListenerAdapter
(_callbacks));
_callbacks = null;
}
_listener = null;
}
private boolean startCallback(MetaDataTag callback, Attributes attrs)
throws SAXException {
if (!isMetaDataMode())
return false;
int[] events = MetaDataParsers.getEventTypes(callback);
if (events == null)
return false;
boolean system = currentElement() == null;
Class type = currentElement() == null ? null :
((ClassMetaData) currentElement()).getDescribedType();
if (type == null)
type = Object.class;
if (_callbacks == null) {
_callbacks = (Collection<LifecycleCallbacks>[])
new Collection[LifecycleEvent.ALL_EVENTS.length];
if (!system)
_highs = new int[LifecycleEvent.ALL_EVENTS.length];
}
MetaDataDefaults def = _repos.getMetaDataFactory().getDefaults();
LifecycleCallbacks adapter;
if (_listener != null)
adapter = new BeanLifecycleCallbacks(_listener,
attrs.getValue("method-name"), false, type);
else
adapter = new MethodLifecycleCallbacks(_cls,
attrs.getValue("method-name"), false);
for (int i = 0; i < events.length; i++) {
int event = events[i];
if (_listener != null) {
MetaDataParsers.validateMethodsForSameCallback(_listener,
_callbacks[event], ((BeanLifecycleCallbacks) adapter).
getCallbackMethod(), callback, def, getLog());
} else {
MetaDataParsers.validateMethodsForSameCallback(_cls,
_callbacks[event], ((MethodLifecycleCallbacks) adapter).
getCallbackMethod(), callback, def, getLog());
}
if (_callbacks[event] == null)
_callbacks[event] = new ArrayList<LifecycleCallbacks>(3);
_callbacks[event].add(adapter);
if (!system && _listener != null)
_highs[event]++;
}
return true;
}
/**
* Store lifecycle metadata.
*/
private void storeCallbacks(ClassMetaData cls) {
LifecycleMetaData meta = cls.getLifecycleMetaData();
Class supCls = cls.getDescribedType().getSuperclass();
Collection<LifecycleCallbacks>[] supCalls = null;
if (!Object.class.equals(supCls)) {
supCalls = AnnotationPersistenceMetaDataParser.parseCallbackMethods
(supCls, null, true, false, _repos);
}
if (supCalls != null) {
for (int event : LifecycleEvent.ALL_EVENTS) {
if (supCalls[event] == null)
continue;
meta.setNonPCSuperclassCallbacks(event, supCalls[event].toArray
(new LifecycleCallbacks[supCalls[event].size()]), 0);
}
}
if (_callbacks == null)
return;
for (int event : LifecycleEvent.ALL_EVENTS) {
if (_callbacks[event] == null)
continue;
meta.setDeclaredCallbacks(event, (LifecycleCallbacks[])
_callbacks[event].toArray
(new LifecycleCallbacks[_callbacks[event].size()]),
_highs[event]);
}
_callbacks = null;
_highs = null;
}
/**
* Instantiate the given class, taking into account the default package.
*/
protected Class classForName(String name)
throws SAXException {
if ("Entity".equals(name))
return PersistenceCapable.class;
return super.classForName(name, isRuntime());
}
}