blob: cc015759d19b607dc7113b2415c948888fce626d [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 static javax.persistence.CascadeType.DETACH;
import static javax.persistence.CascadeType.MERGE;
import static javax.persistence.CascadeType.PERSIST;
import static javax.persistence.CascadeType.REFRESH;
import static javax.persistence.CascadeType.REMOVE;
import static org.apache.openjpa.meta.MetaDataModes.MODE_MAPPING;
import static org.apache.openjpa.meta.MetaDataModes.MODE_META;
import static org.apache.openjpa.meta.MetaDataModes.MODE_NONE;
import static org.apache.openjpa.meta.MetaDataModes.MODE_QUERY;
import static org.apache.openjpa.persistence.MetaDataTag.DATASTORE_ID;
import static org.apache.openjpa.persistence.MetaDataTag.DATA_CACHE;
import static org.apache.openjpa.persistence.MetaDataTag.EMBEDDED_ID;
import static org.apache.openjpa.persistence.MetaDataTag.ENTITY_LISTENERS;
import static org.apache.openjpa.persistence.MetaDataTag.EXCLUDE_DEFAULT_LISTENERS;
import static org.apache.openjpa.persistence.MetaDataTag.EXCLUDE_SUPERCLASS_LISTENERS;
import static org.apache.openjpa.persistence.MetaDataTag.EXTERNALIZER;
import static org.apache.openjpa.persistence.MetaDataTag.EXTERNAL_VAL;
import static org.apache.openjpa.persistence.MetaDataTag.EXTERNAL_VALS;
import static org.apache.openjpa.persistence.MetaDataTag.FACTORY;
import static org.apache.openjpa.persistence.MetaDataTag.FETCH_ATTRIBUTE;
import static org.apache.openjpa.persistence.MetaDataTag.FETCH_GROUP;
import static org.apache.openjpa.persistence.MetaDataTag.FETCH_GROUPS;
import static org.apache.openjpa.persistence.MetaDataTag.FLUSH_MODE;
import static org.apache.openjpa.persistence.MetaDataTag.GENERATED_VALUE;
import static org.apache.openjpa.persistence.MetaDataTag.ID;
import static org.apache.openjpa.persistence.MetaDataTag.ID_CLASS;
import static org.apache.openjpa.persistence.MetaDataTag.LOB;
import static org.apache.openjpa.persistence.MetaDataTag.MAPPED_BY_ID;
import static org.apache.openjpa.persistence.MetaDataTag.MAP_KEY;
import static org.apache.openjpa.persistence.MetaDataTag.MAP_KEY_CLASS;
import static org.apache.openjpa.persistence.MetaDataTag.NATIVE_QUERY;
import static org.apache.openjpa.persistence.MetaDataTag.OPENJPA_VERSION;
import static org.apache.openjpa.persistence.MetaDataTag.ORDER_BY;
import static org.apache.openjpa.persistence.MetaDataTag.ORDER_COLUMN;
import static org.apache.openjpa.persistence.MetaDataTag.POST_LOAD;
import static org.apache.openjpa.persistence.MetaDataTag.POST_PERSIST;
import static org.apache.openjpa.persistence.MetaDataTag.POST_REMOVE;
import static org.apache.openjpa.persistence.MetaDataTag.POST_UPDATE;
import static org.apache.openjpa.persistence.MetaDataTag.PRE_PERSIST;
import static org.apache.openjpa.persistence.MetaDataTag.PRE_REMOVE;
import static org.apache.openjpa.persistence.MetaDataTag.PRE_UPDATE;
import static org.apache.openjpa.persistence.MetaDataTag.QUERY;
import static org.apache.openjpa.persistence.MetaDataTag.QUERY_HINT;
import static org.apache.openjpa.persistence.MetaDataTag.QUERY_STRING;
import static org.apache.openjpa.persistence.MetaDataTag.READ_ONLY;
import static org.apache.openjpa.persistence.MetaDataTag.REFERENCED_FETCH_GROUP;
import static org.apache.openjpa.persistence.MetaDataTag.SEQ_GENERATOR;
import static org.apache.openjpa.persistence.MetaDataTag.VERSION;
import static org.apache.openjpa.persistence.PersistenceStrategy.BASIC;
import static org.apache.openjpa.persistence.PersistenceStrategy.ELEM_COLL;
import static org.apache.openjpa.persistence.PersistenceStrategy.EMBEDDED;
import static org.apache.openjpa.persistence.PersistenceStrategy.MANY_MANY;
import static org.apache.openjpa.persistence.PersistenceStrategy.MANY_ONE;
import static org.apache.openjpa.persistence.PersistenceStrategy.ONE_MANY;
import static org.apache.openjpa.persistence.PersistenceStrategy.ONE_ONE;
import static org.apache.openjpa.persistence.PersistenceStrategy.PERS;
import static org.apache.openjpa.persistence.PersistenceStrategy.PERS_COLL;
import static org.apache.openjpa.persistence.PersistenceStrategy.PERS_MAP;
import static org.apache.openjpa.persistence.PersistenceStrategy.TRANSIENT;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.Serializable;
import java.lang.reflect.Field;
import java.lang.reflect.Member;
import java.lang.reflect.Method;
import java.net.URL;
import java.util.ArrayList;
import java.util.Collection;
import java.util.EnumSet;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Set;
import java.util.Stack;
import javax.persistence.CascadeType;
import javax.persistence.GenerationType;
import javax.persistence.LockModeType;
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.meta.SourceTracker;
import org.apache.openjpa.lib.meta.XMLVersionParser;
import org.apache.openjpa.lib.util.Localizer;
import org.apache.openjpa.lib.util.StringUtil;
import org.apache.openjpa.meta.AccessCode;
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.MetaDataContext;
import org.apache.openjpa.meta.MetaDataDefaults;
import org.apache.openjpa.meta.MetaDataFactory;
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.UpdateStrategies;
import org.apache.openjpa.meta.ValueMetaData;
import org.apache.openjpa.persistence.AnnotationPersistenceMetaDataParser.FetchAttributeImpl;
import org.apache.openjpa.persistence.AnnotationPersistenceMetaDataParser.FetchGroupImpl;
import org.apache.openjpa.util.ImplHelper;
import org.apache.openjpa.util.InternalException;
import org.apache.openjpa.util.MetaDataException;
import org.xml.sax.Attributes;
import org.xml.sax.Locator;
import org.xml.sax.SAXException;
/**
* Custom SAX parser used by the system to quickly parse persistence
* metadata files. This parser may invoke
* {@linkplain AnnotationPersistenceMetaDataParser another parser} to scan
* source code annotation.
*
* @author Steve Kim
* @author Pinaki Poddar
*/
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_CASCADE_DET = "cascade-detach";
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";
protected static final String ELEM_DELIM_IDS = "delimited-identifiers";
// The following is needed for input into the delimitString() method
protected static enum localDBIdentifiers {
SEQUENCE_GEN_SEQ_NAME,
SEQUENCE_GEN_SCHEMA
}
private static final Map<String, Object> _elems =
new HashMap<>();
// Map for storing deferred metadata which needs to be populated
// after embeddables are loaded.
private static final Map<Class<?>, ArrayList<MetaDataContext>>
_embeddables = new HashMap<>();
private static final Map<Class<?>, Integer>
_embeddableAccess = new HashMap<>();
// Hold fetch group info
private FetchGroupImpl[] _fgs = null;
private List<FetchGroupImpl> _fgList = null;
private List<String> _referencedFgList = null;
private FetchGroupImpl _currentFg = null;
private List<FetchAttributeImpl> _fetchAttrList = null;
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_CASCADE_DET, ELEM_CASCADE_DET);
_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(ELEM_DELIM_IDS, ELEM_DELIM_IDS);
_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("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("maps-id", MAPPED_BY_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("order-column", ORDER_COLUMN);
_elems.put("lob", LOB);
_elems.put("data-store-id", DATASTORE_ID);
_elems.put("data-cache", DATA_CACHE);
_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);
_elems.put("element-collection", ELEM_COLL);
_elems.put("persistent", PERS);
_elems.put("persistent-collection", PERS_COLL);
_elems.put("persistent-map", PERS_MAP);
_elems.put("map-key-class", MAP_KEY_CLASS);
_elems.put("read-only", READ_ONLY);
_elems.put("external-values", EXTERNAL_VALS);
_elems.put("external-value", EXTERNAL_VAL);
_elems.put("externalizer", EXTERNALIZER);
_elems.put("factory", FACTORY);
_elems.put("fetch-groups", FETCH_GROUPS);
_elems.put("fetch-group", FETCH_GROUP);
_elems.put("fetch-attribute", FETCH_ATTRIBUTE);
_elems.put("referenced-fetch-group", REFERENCED_FETCH_GROUP);
_elems.put("openjpa-version", OPENJPA_VERSION);
}
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<Object> _elements = new Stack<>();
private final Stack<Object> _parents = new Stack<>();
private StringBuffer _externalValues = null;
protected Class<?> _cls = null;
// List of classes currently being parsed
private ArrayList<Class<?>> _parseList = new ArrayList<>();
private int _fieldPos = 0;
private int _clsPos = 0;
private int _access = AccessCode.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 Collection<Class<?>> _listeners = null;
private int[] _highs = null;
private boolean _isXMLMappingMetaDataComplete = false;
private String _ormVersion;
private String _schemaLocation;
private static final String ORM_XSD_1_0 = "orm_1_0.xsd";
private static final String ORM_XSD_2_0 = "orm_2_0.xsd";
private static final String ORM_XSD_2_1 = "orm_2_1.xsd";
private static final String ORM_XSD_2_2 = "orm_2_2.xsd";
/**
* 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.
*/
@Override
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() & MetaDataRepository.VALIDATE_RUNTIME) != 0)
setParseComments(false);
if (repos != null) {
// Determine if the Thread Context Classloader needs to be temporally overridden to the Classloader
// that loaded the OpenJPA classes, to avoid a potential deadlock issue with the way Xerces
// handles parsers and classloaders.
this.setOverrideContextClassloader(repos.getConfiguration().getCompatibilityInstance().
getOverrideContextClassloader());
}
}
/**
* 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.
*/
@Override
public void setMode(int mode) {
_mode = mode;
if (_parser != null)
_parser.setMode(mode);
}
@Override
public void parse(URL url) throws IOException {
// peek at the doc to determine the version
XMLVersionParser vp = new XMLVersionParser("entity-mappings");
try {
vp.parse(url);
_ormVersion = vp.getVersion();
_schemaLocation = vp.getSchemaLocation();
} catch (Throwable t) {
Log log = getLog();
if (log.isInfoEnabled())
log.trace(_loc.get("version-check-error",
url.toString()));
}
super.parse(url);
}
@Override
public void parse(File file) throws IOException {
// peek at the doc to determine the version
XMLVersionParser vp = new XMLVersionParser("entity-mappings");
try {
vp.parse(file);
_ormVersion = vp.getVersion();
_schemaLocation = vp.getSchemaLocation();
} catch (Throwable t) {
Log log = getLog();
if (log.isInfoEnabled())
log.trace(_loc.get("version-check-error",
file.toString()));
}
super.parse(file);
}
/**
* 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();
}
/**
* Peek a parse element from the stack.
*/
protected Object peekElement() {
return _elements.peek();
}
/**
* 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() {
// use the latest schema by default. 'unknown' docs should parse
// with the latest schema.
String ormxsd = "orm_2_0-xsd.rsrc";
boolean useExtendedSchema = true;
// if the version and/or schema location is for 1.0, use the 1.0
// schema
if (XMLVersionParser.VERSION_1_0.equals(_ormVersion)
|| (_schemaLocation != null && _schemaLocation.indexOf(ORM_XSD_1_0) > -1)) {
ormxsd = "orm-xsd.rsrc";
useExtendedSchema = false;
} else if (XMLVersionParser.VERSION_2_0.equals(_ormVersion)
|| (_schemaLocation != null && _schemaLocation.indexOf(ORM_XSD_2_0) > -1)) {
ormxsd = "orm_2_0-xsd.rsrc";
} else if (XMLVersionParser.VERSION_2_1.equals(_ormVersion)
|| (_schemaLocation != null && _schemaLocation.indexOf(ORM_XSD_2_1) > -1)) {
ormxsd = "orm_2_1.xsd.rsrc";
} else if (XMLVersionParser.VERSION_2_2.equals(_ormVersion)
|| (_schemaLocation != null && _schemaLocation.indexOf(ORM_XSD_2_2) > -1)) {
ormxsd = "orm_2_2.xsd.rsrc";
}
List<InputStream> schema = new ArrayList<>();
schema.add(XMLPersistenceMetaDataParser.class.getResourceAsStream(ormxsd));
if (useExtendedSchema) {
// Get the extendable schema
InputStream extendableXSDIS =
XMLPersistenceMetaDataParser.class.getResourceAsStream("extendable-orm.xsd");
if (extendableXSDIS != null) {
schema.add(extendableXSDIS);
}
else {
// TODO: log/trace
}
// Get the openjpa extended schema
InputStream openjpaXSDIS =
XMLPersistenceMetaDataParser.class.getResourceAsStream("openjpa-orm.xsd");
if (openjpaXSDIS != null) {
schema.add(openjpaXSDIS);
}
else {
// TODO: log/trace
}
}
return schema.toArray();
}
@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() {
// Add all remaining deferred embeddable metadata
addDeferredEmbeddableMetaData();
super.reset();
_elements.clear();
_parents.clear();
_cls = null;
_parseList.clear();
_fieldPos = 0;
_clsPos = 0;
_access = AccessCode.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 = _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);
_isXMLMappingMetaDataComplete = true;
}
else if (tag == ELEM_ACCESS)
ret = _mode != MODE_QUERY;
else if (tag == ELEM_LISTENER)
ret = startEntityListener(attrs);
else if (tag == ELEM_DELIM_IDS)
ret = startDelimitedIdentifiers();
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 || tag == ELEM_CASCADE_DET)
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 MAP_KEY_CLASS:
ret = startMapKeyClass(attrs);
break;
case FLUSH_MODE:
ret = startFlushMode(attrs);
break;
case ORDER_COLUMN:
ret = startOrderColumn(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;
case DATASTORE_ID:
ret = startDatastoreId(attrs);
break;
case DATA_CACHE:
ret = startDataCache(attrs);
break;
case READ_ONLY:
ret = startReadOnly(attrs);
break;
case EXTERNAL_VALS:
ret = startExternalValues(attrs);
break;
case EXTERNAL_VAL:
ret = startExternalValue(attrs);
break;
case EXTERNALIZER:
ret = startExternalizer(attrs);
break;
case FACTORY:
ret = startFactory(attrs);
break;
case FETCH_GROUPS:
ret = startFetchGroups(attrs);
break;
case FETCH_GROUP:
ret = startFetchGroup(attrs);
break;
case FETCH_ATTRIBUTE:
ret = startFetchAttribute(attrs);
break;
case REFERENCED_FETCH_GROUP:
ret = startReferencedFetchGroup(attrs);
break;
case OPENJPA_VERSION:
ret = true;
// TODO: right now the schema enforces this value, but may need to change in the future
break;
default:
warnUnsupportedTag(name);
}
} else if (tag instanceof PersistenceStrategy) {
PersistenceStrategy ps = (PersistenceStrategy) tag;
if (_openjpaNamespace > 0) {
if (ps == PERS
|| ps == PERS_COLL
|| ps == PERS_MAP)
ret = startStrategy(ps, attrs);
else
ret = startExtendedStrategy(ps, attrs);
}
else {
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 || tag == ELEM_CASCADE_DET)
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;
case EXTERNAL_VALS:
endExternalValues();
break;
case EXTERNALIZER:
endExternalizer();
break;
case FACTORY:
endFactory();
break;
case FETCH_GROUP:
endFetchGroup();
break;
case REFERENCED_FETCH_GROUP:
endReferencedFetchGroup();
break;
}
} else if (tag instanceof PersistenceStrategy) {
PersistenceStrategy ps = (PersistenceStrategy) tag;
if (_openjpaNamespace > 0) {
endExtendedStrategy(ps);
}
else {
endStrategy(ps);
}
}
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 {
}
boolean isMetaDataComplete(Attributes attrs) {
return attrs != null
&& "true".equals(attrs.getValue("metadata-complete"));
}
void resetAnnotationParser() {
setAnnotationParser(((PersistenceMetaDataFactory)getRepository()
.getMetaDataFactory()).getAnnotationParser());
}
@Override
protected boolean startClass(String elem, Attributes attrs)
throws SAXException {
boolean metaDataComplete = false;
super.startClass(elem, attrs);
if (isMetaDataComplete(attrs)) {
metaDataComplete = true;
setAnnotationParser(null);
} else if (!_isXMLMappingMetaDataComplete){
resetAnnotationParser();
}
// query mode only?
_cls = classForName(currentClassName());
// Prevent a reentrant parse for the same class
if (parseListContains(_cls)) {
return false;
}
if (_mode == MODE_QUERY) {
if(_conf.getCompatibilityInstance().getParseAnnotationsForQueryMode()){
if (_parser != null) {
_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(isDuplicateClass(meta)) {
if (log.isWarnEnabled()) {
log.warn(_loc.get("dup-metadata", _cls, getSourceName()));
}
if(log.isTraceEnabled()) {
log.trace(String.format(
"MetaData originally obtained from source: %s under mode: %d with scope: %s, and type: %d",
meta.getSourceName(), meta.getSourceMode(), meta.getSourceScope(), meta.getSourceType()));
}
}
_cls = null;
return false;
}
int access = AccessCode.UNKNOWN;
if (meta == null) {
int accessCode = toAccessType(attrs.getValue("access"));
// if access not specified and access was specified at
// the system level, use the system default (which may
// be UNKNOWN)
if (accessCode == AccessCode.UNKNOWN)
accessCode = _access;
meta = repos.addMetaData(_cls, accessCode, metaDataComplete);
FieldMetaData[] fmds = meta.getFields();
if (metaDataComplete) {
for (FieldMetaData fmd : fmds) {
fmd.setExplicit(true);
}
}
meta.setEnvClassLoader(_envLoader);
meta.setSourceMode(MODE_NONE);
// parse annotations first so XML overrides them
if (_parser != null) {
_parser.parse(_cls);
}
}
access = meta.getAccessType();
boolean mappedSuper = "mapped-superclass".equals(elem);
boolean embeddable = "embeddable".equals(elem);
if (isMetaDataMode()) {
Locator locator = getLocation().getLocator();
meta.setSource(getSourceFile(), SourceTracker.SRC_XML, locator != null ? locator.getSystemId() : "" );
meta.setSourceMode(MODE_META, true);
if (locator != null) {
meta.setLineNumber(locator.getLineNumber());
meta.setColNumber(locator.getColumnNumber());
}
meta.setListingIndex(_clsPos);
String name = attrs.getValue("name");
if (!StringUtil.isEmpty(name))
meta.setTypeAlias(name);
meta.setAbstract(mappedSuper);
meta.setEmbeddedOnly(mappedSuper || embeddable);
if (embeddable) {
meta.setEmbeddable();
setDeferredEmbeddableAccessType(_cls, access);
}
}
if (attrs.getValue("cacheable") != null) {
meta.setCacheEnabled(Boolean.valueOf(attrs.getValue("cacheable")));
}
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());
}
/**
* Parse the given string as an entity access type, defaulting to given
* default if string is empty.
*/
private int toAccessType(String str) {
if (StringUtil.isEmpty(str))
return AccessCode.UNKNOWN;
if ("PROPERTY".equals(str))
return AccessCode.EXPLICIT | AccessCode.PROPERTY;
return AccessCode.EXPLICIT | AccessCode.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");
// Do not normalize the sequence name if it appears to be a plugin
if (seq.indexOf('(') == -1){
seq = normalizeSequenceName(seq);
}
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 schema = normalizeSchemaName(attrs.getValue("schema"));
String catalog = normalizeCatalogName(attrs.getValue("catalog"));
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);
meta.setSchema(schema);
meta.setCatalog(catalog);
Object cur = currentElement();
Object scope = (cur instanceof ClassMetaData)
? ((ClassMetaData) cur).getDescribedType() : null;
meta.setSource(getSourceFile(), scope, SourceTracker.SRC_XML);
Locator locator = getLocation().getLocator();
if (locator != null) {
meta.setLineNumber(locator.getLineNumber());
meta.setColNumber(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);
fmd.setSerialized(false);
if (fmd.getEmbeddedMetaData() == null)
// fmd.addEmbeddedMetaData();
deferEmbeddable(fmd.getDeclaredType(), fmd);
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);
}
if (!Serializable.class.isAssignableFrom(idCls)) {
_conf.getConfigurationLog().warn(_loc.get("id-class-not-serializable", idCls, _cls));
}
meta.setObjectIdType(idCls, true);
return true;
}
protected void endIdClass()
throws SAXException {
}
/**
* Parse lob.
*/
protected boolean startLob(Attributes attrs)
throws SAXException {
FieldMetaData fmd = (FieldMetaData) currentElement();
int typeCode = fmd.isElementCollection() ? fmd.getElement().getDeclaredTypeCode() : fmd.getDeclaredTypeCode();
Class<?> type = fmd.isElementCollection() ? fmd.getElement().getDeclaredType() : fmd.getDeclaredType();
if (typeCode != JavaTypes.STRING
&& type != char[].class
&& type != Character[].class
&& type != byte[].class
&& type != 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 = StringUtil.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;
boolean puDefault = 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;
puDefault = true;
}
boolean all = ELEM_CASCADE_ALL == tag;
if (all || ELEM_CASCADE_PER == tag) {
cascades.add(PERSIST);
if (puDefault) {
MetaDataDefaults mdd = _repos.getMetaDataFactory().getDefaults();
mdd.setDefaultCascadePersistEnabled(true);
}
}
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);
if (all || ELEM_CASCADE_DET == tag)
cascades.add(DETACH);
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;
if (_strategy == ONE_MANY || _strategy == MANY_MANY) {
vmd = fmd.getElement();
}
for (CascadeType cascade : cascades) {
switch (cascade) {
case PERSIST:
vmd.setCascadePersist(ValueMetaData.CASCADE_IMMEDIATE, false);
break;
case MERGE:
vmd.setCascadeAttach(ValueMetaData.CASCADE_IMMEDIATE);
break;
case DETACH:
vmd.setCascadeDetach(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);
int fldAccess = getFieldAccess(field, attrs);
// If the access defined in XML is not the same as what was defined
// by default or annotation, find the appropriate backing member and
// replace what is currently defined in metadata.
if ((field == null || field.getDeclaredType() == Object.class ||
field.getAccessType() != fldAccess)
&& meta.getDescribedType() != Object.class) {
Member member = _repos.getMetaDataFactory().getDefaults()
.getMemberByProperty(meta, name, fldAccess, false);
Class<?> type = Field.class.isInstance(member) ?
((Field)member).getType() : ((Method)member).getReturnType();
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;
}
/**
* Determines access for field based upon existing metadata and XML
* attributes.
*
* @param field FieldMetaData current metadata for field
* @param attrs XML Attributes defined on this field
*/
private int getFieldAccess(FieldMetaData field, Attributes attrs) {
if (attrs != null) {
String access = attrs.getValue("access");
if ("PROPERTY".equals(access))
return AccessCode.EXPLICIT | AccessCode.PROPERTY;
if ("FIELD".equals(access))
return AccessCode.EXPLICIT | AccessCode.FIELD;
}
// Check access defined on field, if provided
if (field != null) {
return field.getAccessType();
}
// Otherwise, get the default access type of the declaring class
ClassMetaData meta = (ClassMetaData) currentElement();
if (meta != null) {
return AccessCode.toFieldCode(meta.getAccessType());
}
return AccessCode.UNKNOWN;
}
/**
* 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("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:
String val = attrs.getValue("fetch");
if (val != null) {
fmd.setInDefaultFetchGroup("EAGER".equals(val));
}
fmd.setManagement(FieldMetaData.MANAGE_NONE);
break;
case ELEM_COLL:
parseElementCollection(fmd, attrs);
break;
case PERS:
parsePersistent(fmd, attrs);
break;
case PERS_COLL:
parsePersistentCollection(fmd, attrs);
break;
case PERS_MAP:
parsePersistentMap(fmd, attrs);
break;
}
}
/**
* Parse basic.
*/
protected void parseBasic(FieldMetaData fmd, Attributes attrs)
throws SAXException {
String val = attrs.getValue("fetch");
if (val != null) {
fmd.setInDefaultFetchGroup("EAGER".equals(val));
}
}
/**
* Parse embedded.
*/
protected void parseEmbedded(FieldMetaData fmd, Attributes attrs)
throws SAXException {
assertPC(fmd, "Embedded");
fmd.setInDefaultFetchGroup(true);
fmd.setEmbedded(true);
fmd.setSerialized(false); // override any Lob annotation
if (fmd.getEmbeddedMetaData() == null)
// fmd.addEmbeddedMetaData();
deferEmbeddable(fmd.getDeclaredType(), fmd);
}
/**
* 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("fetch");
boolean dfg = (val != null && val.equals("LAZY")) ? false : true;
// We need to toggle the DFG explicit flag here because this is used for an optimization when selecting an
// Entity with lazy fields.
fmd.setDefaultFetchGroupExplicit(true);
fmd.setInDefaultFetchGroup(dfg);
fmd.setDefaultFetchGroupExplicit(false);
val = attrs.getValue("target-entity");
if (val != null)
fmd.setTypeOverride(AnnotationPersistenceMetaDataParser.toOverrideType(classForName(val)));
assertPC(fmd, "OneToOne");
fmd.setSerialized(false); // override any Lob annotation
boolean orphanRemoval = Boolean.valueOf(attrs.getValue(
"orphan-removal"));
setOrphanRemoval(fmd, orphanRemoval);
String mapsId = attrs.getValue("maps-id");
if (mapsId != null)
fmd.setMappedByIdValue(mapsId);
}
/**
* Parse many-to-one.
*/
protected void parseManyToOne(FieldMetaData fmd, Attributes attrs)
throws SAXException {
String val = attrs.getValue("fetch");
boolean dfg = (val != null && val.equals("LAZY")) ? false : true;
// We need to toggle the DFG explicit flag here because this is used for an optimization when selecting an
// Entity with lazy fields.
fmd.setDefaultFetchGroupExplicit(true);
fmd.setInDefaultFetchGroup(dfg);
fmd.setDefaultFetchGroupExplicit(false);
val = attrs.getValue("target-entity");
if (val != null)
fmd.setTypeOverride(AnnotationPersistenceMetaDataParser.toOverrideType(classForName(val)));
assertPC(fmd, "ManyToOne");
fmd.setSerialized(false); // override any Lob annotation
String mapsId = attrs.getValue("maps-id");
if (mapsId != null)
fmd.setMappedByIdValue(mapsId);
}
/**
* Parse many-to-many.
*/
protected void parseManyToMany(FieldMetaData fmd, Attributes attrs)
throws SAXException {
String val = attrs.getValue("fetch");
if (val != null) {
fmd.setInDefaultFetchGroup("EAGER".equals(val));
}
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("fetch");
if (val != null) {
fmd.setInDefaultFetchGroup("EAGER".equals(val));
}
val = attrs.getValue("target-entity");
if (val != null)
fmd.getElement().setDeclaredType(classForName(val));
assertPCCollection(fmd, "OneToMany");
fmd.setSerialized(false); // override any Lob annotation
boolean orphanRemoval = Boolean.valueOf(attrs.getValue(
"orphan-removal"));
setOrphanRemoval(fmd.getElement(), orphanRemoval);
}
protected void setOrphanRemoval(ValueMetaData vmd, boolean orphanRemoval) {
if (orphanRemoval)
vmd.setCascadeDelete(ValueMetaData.CASCADE_AUTO);
}
protected void parseElementCollection(FieldMetaData fmd, Attributes attrs)
throws SAXException {
String val = attrs.getValue("target-class");
if (val != null)
fmd.getElement().setDeclaredType(classForName(val));
if (fmd.getDeclaredTypeCode() != JavaTypes.COLLECTION &&
fmd.getDeclaredTypeCode() != JavaTypes.MAP)
throw getException(_loc.get("bad-meta-anno", fmd,
"ElementCollection"));
val = attrs.getValue("fetch");
if (val != null)
fmd.setInDefaultFetchGroup("EAGER".equals(val));
fmd.setElementCollection(true);
fmd.setSerialized(false);
if (JavaTypes.maybePC(fmd.getElement()) && !fmd.getElement().getDeclaredType().isEnum()) {
fmd.getElement().setEmbedded(true);
if (fmd.getElement().getEmbeddedMetaData() == null)
// fmd.getElement().addEmbeddedMetaData();
deferEmbeddable(fmd.getElement().getDeclaredType(),
fmd.getElement());
}
}
/**
* 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 map-key-class.
*/
private boolean startMapKeyClass(Attributes attrs)
throws SAXException {
if (!isMappingOverrideMode())
return false;
FieldMetaData fmd = (FieldMetaData) currentElement();
String mapKeyClass = attrs.getValue("class");
if (mapKeyClass != null) {
try {
fmd.getKey().setDeclaredType(Class.forName(mapKeyClass));
} catch (ClassNotFoundException e) {
throw new IllegalArgumentException("Class not found");
}
} else
throw new IllegalArgumentException(
"The value of the MapKeyClass cannot be null");
return true;
}
/**
* Parse order-by.
*/
private void endOrderBy()
throws SAXException {
FieldMetaData fmd = (FieldMetaData) currentElement();
String dec = currentText();
if (fmd.isElementCollection() &&
fmd.getElement().getEmbeddedMetaData() != null ||
isDeferredEmbeddable(fmd.getElement().getDeclaredType(),
fmd.getElement())) {
if (dec.length() == 0 || dec.equals("ASC") ||
dec.equals("DESC"))
throw new MetaDataException(_loc.get(
"invalid-orderBy", fmd));
}
if (StringUtil.isEmpty(dec) || dec.equals("ASC"))
dec = Order.ELEMENT + " asc";
else if (dec.equals("DESC"))
dec = Order.ELEMENT + " desc";
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().searchQueryMetaDataByName(name);
if (meta != null) {
Class<?> defType = meta.getDefiningType();
if ((defType != _cls) && log.isWarnEnabled()) {
log.warn(_loc.get("dup-query", name, currentLocation(),
defType));
}
pushElement(meta);
return true;
}
meta = getRepository().addQueryMetaData(null, name);
meta.setDefiningType(_cls);
meta.setLanguage(JPQLParser.LANG_JPQL);
meta.setQueryString(attrs.getValue("query"));
String lockModeStr = attrs.getValue("lock-mode");
LockModeType lmt = processNamedQueryLockModeType(log, lockModeStr, name);
if (lmt != null && lmt != LockModeType.NONE) {
meta.addHint("openjpa.FetchPlan.ReadLockMode", lmt);
}
Locator locator = getLocation().getLocator();
if (locator != null) {
meta.setLineNumber(locator.getLineNumber());
meta.setColNumber(locator.getColumnNumber());
}
Object cur = currentElement();
Object scope = (cur instanceof ClassMetaData)
? ((ClassMetaData) cur).getDescribedType() : null;
meta.setSource(getSourceFile(), scope, SourceTracker.SRC_XML, locator == null ? "" : locator.getSystemId());
if (isMetaDataMode())
meta.setSourceMode(MODE_META);
else if (isMappingMode())
meta.setSourceMode(MODE_MAPPING);
else
meta.setSourceMode(MODE_QUERY);
pushElement(meta);
return true;
}
/**
* A private worker method that calculates the lock mode for an individual NamedQuery. If the NamedQuery is
* configured to use the NONE lock mode(explicit or implicit), this method will promote the lock to a READ
* level lock. This was done to allow for JPA1 apps to function properly under a 2.0 runtime.
*/
private LockModeType processNamedQueryLockModeType(Log log, String lockModeString, String queryName) {
if (lockModeString == null) {
return null;
}
LockModeType lmt = LockModeType.valueOf(lockModeString);
String lm = _conf.getLockManager();
boolean optimistic = _conf.getOptimistic();
if (lm != null) {
lm = lm.toLowerCase();
if (lm.contains("pessimistic")) {
if (lmt == LockModeType.NONE && !optimistic) {
if (log != null && log.isWarnEnabled()) {
log.warn(_loc.get("override-named-query-lock-mode", new String[] { "xml", queryName,
_cls.getName() }));
}
lmt = LockModeType.READ;
}
}
}
return lmt;
}
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(name);
if (meta != null && isDuplicateQuery(meta) ) {
log.warn(_loc.get("override-query", name, currentLocation()));
}
meta = getRepository().addQueryMetaData(null, name);
meta.setDefiningType(_cls);
meta.setLanguage(QueryLanguages.LANG_SQL);
meta.setQueryString(attrs.getValue("query"));
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;
Locator locator = getLocation().getLocator();
meta.setSource(getSourceFile(), scope, SourceTracker.SRC_XML, locator == null ? "" : locator.getSystemId());
if (locator != null) {
meta.setLineNumber(locator.getLineNumber());
meta.setColNumber(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"));
if (!_conf.getCallbackOptionsInstance().getAllowsDuplicateListener()) {
if (_listeners == null)
_listeners = new ArrayList<>();
if (_listeners.contains(_listener))
return true;
_listeners.add(_listener);
}
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, _conf);
if (events == null)
return false;
boolean system = currentElement() == null;
// If in a multi-level parse, do not add system level listeners.
// Otherwise, they will get added multiple times.
if (system && _parseList != null && _parseList.size() > 0) {
return false;
}
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];
}
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 event : events) {
if (_listener != null) {
MetaDataParsers.validateMethodsForSameCallback(_listener,
_callbacks[event], ((BeanLifecycleCallbacks) adapter).
getCallbackMethod(), callback, _conf, getLog());
}
else {
MetaDataParsers.validateMethodsForSameCallback(_cls,
_callbacks[event], ((MethodLifecycleCallbacks) adapter).
getCallbackMethod(), callback, _conf, getLog());
}
if (_callbacks[event] == null)
_callbacks[event] = new ArrayList<>(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;
}
protected boolean startOrderColumn(Attributes attrs)
throws SAXException {
return true;
}
/**
* 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());
}
/**
* Process all deferred embeddables using an unknown access type.
*/
protected void addDeferredEmbeddableMetaData() {
if (_embeddables != null && _embeddables.size() > 0) {
// Reverse iterate the array of remaining deferred embeddables
// since elements will be removed as they are processed.
Class<?>[] classes = _embeddables.keySet().toArray(
new Class<?>[_embeddables.size()]);
for (int i = classes.length - 1 ; i >= 0; i--) {
try {
Integer access = _embeddableAccess.get(classes[i]);
if (access == null) {
access = AccessCode.UNKNOWN;
}
addDeferredEmbeddableMetaData(classes[i],
access);
}
catch (Exception e) {
throw new MetaDataException(
_loc.get("no-embeddable-metadata",
classes[i].getName()), e);
}
}
}
}
/**
* Process all deferred embeddables and embeddable mapping overrides
* for a given class. This should only happen after the access type
* of the embeddable is known.
*
* @param embedType embeddable class
* @param access class level access for embeddable
* @throws SAXException
*/
protected void addDeferredEmbeddableMetaData(Class<?> embedType,
int access) throws SAXException {
ArrayList<MetaDataContext> fmds = _embeddables.get(embedType);
if (fmds != null && fmds.size() > 0) {
for (MetaDataContext md : fmds) {
if (md instanceof FieldMetaData) {
FieldMetaData fmd = (FieldMetaData)md;
fmd.addEmbeddedMetaData(access);
}
else if (md instanceof ValueMetaData) {
ValueMetaData vmd = (ValueMetaData)md;
vmd.addEmbeddedMetaData(access);
}
}
applyDeferredEmbeddableOverrides(embedType);
// Clean up deferrals after they have been processed
fmds.clear();
_embeddables.remove(embedType);
}
}
protected void setDeferredEmbeddableAccessType(Class<?> embedType,
int access) {
_embeddableAccess.put(embedType, access);
}
/*
* Clear any deferred metadata
*/
@Override
protected void clearDeferredMetaData() {
_embeddables.clear();
_embeddableAccess.clear();
}
/*
* Determines whether the embeddable type is deferred.
*/
protected boolean isDeferredEmbeddable(Class<?> embedType,
MetaDataContext fmd) {
ArrayList<MetaDataContext> fmds = _embeddables.get(embedType);
if (fmds != null) {
return fmds.contains(fmd);
}
return false;
}
/*
* Add the fmd to the defer list for for the given embeddable type
*/
protected void deferEmbeddable(Class<?> embedType, MetaDataContext fmd) {
ArrayList<MetaDataContext> fmds = _embeddables.computeIfAbsent(embedType, k -> new ArrayList<>());
fmds.add(fmd);
}
/*
* Apply any deferred overrides.
*/
protected void applyDeferredEmbeddableOverrides(Class<?> cls)
throws SAXException {
}
/*
* Add the array of classes to the active parse list.
*/
public void addToParseList(ArrayList<Class<?>> parseList) {
if (parseList == null)
return;
_parseList.addAll(parseList);
}
/*
* Add the class to the active parse list.
*/
public void addToParseList(Class<?> parentCls) {
if (parentCls == null)
return;
_parseList.add(parentCls);
}
/*
* Whether the active parse list contains the specified class.
*/
public boolean parseListContains(Class<?> cls) {
if (_parseList.size() == 0)
return false;
return _parseList.contains(cls);
}
/*
* Returns the list of classes actively being parsed.
*/
public ArrayList<Class<?>> getParseList() {
return _parseList;
}
/*
* Returns class currently being parsed.
*/
public Class<?> getParseClass() {
return _cls;
}
protected boolean startDelimitedIdentifiers() {
return false;
}
protected String normalizeSequenceName(String seqName) {
return seqName;
}
protected String normalizeSchemaName(String schName) {
return schName;
}
protected String normalizeCatalogName(String catName) {
return catName;
}
/**
* Determines whether the ClassMetaData has been resolved more than once. Compares the current sourceName and
* linenumber to the ones used to originally resolve the metadata.
*
* @param meta The ClassMetaData to inspect.
* @return true if the source was has already been resolved from a different location. Otherwise return false
*/
protected boolean isDuplicateClass(ClassMetaData meta) {
if (!Objects.equals(getSourceName(), meta.getSourceName())) {
return true;
}
if (getLineNum() != meta.getLineNumber()) {
return true;
}
return false;
}
/**
* Determines whether the QueryMetaData has been resolved more than once.
* @param meta QueryMetaData that has already been resolved.
* @return true if the QueryMetaData was defined in a different place - e.g. another line in orm.xml.
*/
protected boolean isDuplicateQuery(QueryMetaData meta) {
if(! Objects.equals(getSourceName(), meta.getSourceName())) {
return true;
}
if(getLineNum() != meta.getLineNumber()) {
return true;
}
return false;
}
private int getLineNum() {
int lineNum = 0;
Locator loc = getLocation().getLocator();
if(loc != null ) {
lineNum = loc.getLineNumber();
}
return lineNum;
}
private boolean startDatastoreId(Attributes attrs)
throws SAXException {
MetaDataRepository repos = getRepository();
ClassMetaData meta = repos.getCachedMetaData(_cls);
//Set default value if not specified
String strategy = attrs.getValue("strategy");
if (StringUtil.isEmpty(strategy)) {
strategy ="AUTO" ;
}
GenerationType stratType = GenerationType.valueOf(strategy);
AnnotationPersistenceMetaDataParser.parseDataStoreId(meta, stratType,
attrs.getValue("generator"));
return true;
}
private boolean startDataCache(Attributes attrs)
throws SAXException {
String enabledStr = attrs.getValue("enabled");
boolean enabled = (Boolean) (StringUtil.isEmpty(enabledStr) ? true :
Boolean.parseBoolean(enabledStr));
String timeoutStr = attrs.getValue("timeout");
int timeout = (Integer) (StringUtil.isEmpty(timeoutStr) ? Integer.MIN_VALUE :
Integer.parseInt(timeoutStr));
String name = attrs.getValue("name");
name = StringUtil.isEmpty(name) ? "" : name;
AnnotationPersistenceMetaDataParser.parseDataCache(getRepository().getCachedMetaData(_cls),
enabled, name, timeout);
return true;
}
private boolean startExtendedStrategy(PersistenceStrategy ps, Attributes attrs)
throws SAXException {
FieldMetaData fmd = (FieldMetaData) currentElement();
parseExtendedStrategy(fmd, ps, attrs);
return true;
}
private void endExtendedStrategy(PersistenceStrategy ps)
throws SAXException {
if (ps == PERS
|| ps == PERS_COLL
|| ps == PERS_MAP) {
finishField();
}
}
/**
* Parse strategy specific attributes.
*/
private void parseExtendedStrategy(FieldMetaData fmd,
PersistenceStrategy strategy, Attributes attrs)
throws SAXException {
// The following attributes will be temporarily parsed for all strategy types. This
// is because it is not clear which attributes should be supported for which strategies.
// And more testing needs to be done to determine what actually works.
// Right now they are limited by the schema. But, putting these here allows a temporary schema
// update by a developer without requiring a corresponding code update.
parseTypeAttr(fmd, attrs);
parseElementTypeAttr(fmd, attrs);
parseKeyTypeAttr(fmd, attrs);
parseDependentAttr(fmd, attrs);
parseElementDependentAttr(fmd, attrs);
parseKeyDependentAttr(fmd, attrs);
parseElementClassCriteriaAttr(fmd, attrs);
parseLRSAttr(fmd, attrs);
parseInverseLogicalAttr(fmd, attrs);
parseEagerFetchModeAttr(fmd, attrs);
switch (strategy) {
case BASIC:
parseExtendedBasic(fmd, attrs);
break;
case EMBEDDED:
parseExtendedEmbedded(fmd, attrs);
break;
case ONE_ONE:
parseExtendedOneToOne(fmd, attrs);
break;
case MANY_ONE:
parseExtendedManyToOne(fmd, attrs);
break;
case MANY_MANY:
parseExtendedManyToMany(fmd, attrs);
break;
case ONE_MANY:
parseExtendedOneToMany(fmd, attrs);
break;
case ELEM_COLL:
parseExtendedElementCollection(fmd, attrs);
}
}
private void parseExtendedBasic(FieldMetaData fmd, Attributes attrs)
throws SAXException {
parseCommonExtendedAttributes(fmd, attrs);
// TODO: Handle specific attributes
}
private void parseExtendedEmbedded(FieldMetaData fmd, Attributes attrs)
throws SAXException {
parseCommonExtendedAttributes(fmd, attrs);
// TODO: Handle specific attributes
}
private void parseExtendedOneToOne(FieldMetaData fmd, Attributes attrs)
throws SAXException {
parseCommonExtendedAttributes(fmd, attrs);
// TODO: Handle specific attributes
}
private void parseExtendedManyToOne(FieldMetaData fmd, Attributes attrs)
throws SAXException {
parseCommonExtendedAttributes(fmd, attrs);
// TODO: Handle specific attributes
}
private void parseExtendedManyToMany(FieldMetaData fmd, Attributes attrs)
throws SAXException {
parseCommonExtendedAttributes(fmd, attrs);
// TODO: Handle specific attributes
}
private void parseExtendedOneToMany(FieldMetaData fmd, Attributes attrs)
throws SAXException {
parseCommonExtendedAttributes(fmd, attrs);
// TODO: Handle specific attributes
}
private void parseExtendedElementCollection(FieldMetaData fmd, Attributes attrs)
throws SAXException {
parseCommonExtendedAttributes(fmd, attrs);
// TODO: Handle specific attributes
}
private void parsePersistent(FieldMetaData fmd, Attributes attrs)
throws SAXException {
parseCommonExtendedAttributes(fmd, attrs);
parseTypeAttr(fmd, attrs);
// TODO - handle attributes
String val = attrs.getValue("fetch");
if (val != null) {
fmd.setInDefaultFetchGroup("EAGER".equals(val));
}
switch (fmd.getDeclaredTypeCode()) {
case JavaTypes.ARRAY:
if (fmd.getDeclaredType() == byte[].class
|| fmd.getDeclaredType() == Byte[].class
|| fmd.getDeclaredType() == char[].class
|| fmd.getDeclaredType() == Character[].class)
break;
// no break
case JavaTypes.COLLECTION:
case JavaTypes.MAP:
throw new MetaDataException(_loc.get("bad-meta-anno", fmd,
"Persistent"));
}
}
private void parsePersistentCollection(FieldMetaData fmd, Attributes attrs)
throws SAXException {
parseCommonExtendedAttributes(fmd, attrs);
parseElementTypeAttr(fmd, attrs);
// TODO - handle attributes and field type
}
private void parsePersistentMap(FieldMetaData fmd, Attributes attrs)
throws SAXException {
parseCommonExtendedAttributes(fmd, attrs);
parseElementTypeAttr(fmd, attrs);
parseKeyTypeAttr(fmd, attrs);
// TODO - handle attributes and field type
}
private void parseCommonExtendedAttributes(FieldMetaData fmd, Attributes attrs) {
String loadFetchGroup = attrs.getValue("load-fetch-group");
if (!StringUtil.isEmpty(loadFetchGroup)) {
fmd.setLoadFetchGroup(loadFetchGroup);
}
String externalizer = attrs.getValue("externalizer");
if (!StringUtil.isEmpty(externalizer)) {
fmd.setExternalizer(externalizer);
}
String factory = attrs.getValue("factory");
if (!StringUtil.isEmpty(factory)) {
fmd.setFactory(factory);
}
parseStrategy(fmd, attrs);
}
protected void parseStrategy(FieldMetaData fmd, Attributes attrs) {
}
private boolean startReadOnly(Attributes attrs)
throws SAXException {
FieldMetaData fmd = (FieldMetaData) currentElement();
String updateAction = attrs.getValue("update-action");
if (updateAction.equalsIgnoreCase("RESTRICT")) {
fmd.setUpdateStrategy(UpdateStrategies.RESTRICT);
}
else if (updateAction.equalsIgnoreCase("IGNORE")) {
fmd.setUpdateStrategy(UpdateStrategies.IGNORE);
}
else
throw new InternalException();
return true;
}
private void parseDependentAttr(FieldMetaData fmd, Attributes attrs)
throws SAXException {
String dependentStr = attrs.getValue("dependent");
if (!StringUtil.isEmpty(dependentStr)) {
boolean dependent = Boolean.parseBoolean(dependentStr);
if (dependent) {
fmd.setCascadeDelete(ValueMetaData.CASCADE_AUTO);
}
else {
fmd.setCascadeDelete(ValueMetaData.CASCADE_NONE);
}
}
}
private void parseElementDependentAttr(FieldMetaData fmd, Attributes attrs)
throws SAXException {
String elementDependentStr = attrs.getValue("element-dependent");
if (!StringUtil.isEmpty(elementDependentStr)) {
boolean elementDependent = Boolean.parseBoolean(elementDependentStr);
if (elementDependent) {
fmd.getElement().setCascadeDelete(ValueMetaData.CASCADE_AUTO);
}
else {
fmd.getElement().setCascadeDelete(ValueMetaData.CASCADE_NONE);
}
}
}
private void parseKeyDependentAttr(FieldMetaData fmd, Attributes attrs)
throws SAXException {
String keyDependentStr = attrs.getValue("key-dependent");
if (!StringUtil.isEmpty(keyDependentStr)) {
boolean keyDependent = Boolean.parseBoolean(keyDependentStr);
if (keyDependent) {
fmd.getKey().setCascadeDelete(ValueMetaData.CASCADE_AUTO);
}
else {
fmd.getKey().setCascadeDelete(ValueMetaData.CASCADE_NONE);
}
}
}
protected void parseElementClassCriteriaAttr(FieldMetaData fmd, Attributes attrs)
throws SAXException {
// String elementClassCriteriaString = attrs.getValue("element-class-criteria");
// if (!StringUtil.isEmpty(elementClassCriteriaString)) {
// FieldMapping fm = (FieldMapping) fmd;
// boolean elementClassCriteria = Boolean.parseBoolean(elementClassCriteriaString);
// fm.getElementMapping().getValueInfo().setUseClassCriteria(elementClassCriteria);
// }
}
private void parseTypeAttr(FieldMetaData fmd, Attributes attrs)
throws SAXException {
String typeStr = attrs.getValue("type");
if (!StringUtil.isEmpty(typeStr)) {
if (StringUtil.endsWithIgnoreCase(typeStr, ".class")) {
typeStr = typeStr.substring(0, typeStr.lastIndexOf('.'));
}
Class<?> typeCls = parseTypeStr(typeStr);
fmd.setTypeOverride(typeCls);
}
}
private void parseLRSAttr(FieldMetaData fmd, Attributes attrs)
throws SAXException {
String lrsStr = attrs.getValue("lrs");
if (!StringUtil.isEmpty(lrsStr)) {
boolean lrs = Boolean.parseBoolean(lrsStr);
fmd.setLRS(lrs);
}
}
private void parseElementTypeAttr(FieldMetaData fmd, Attributes attrs)
throws SAXException {
String typeStr = attrs.getValue("element-type");
if (!StringUtil.isEmpty(typeStr)) {
if (StringUtil.endsWithIgnoreCase(typeStr, ".class")) {
typeStr = typeStr.substring(0, typeStr.lastIndexOf('.'));
}
Class<?> typeCls = parseTypeStr(typeStr);
fmd.setTypeOverride(typeCls);
}
}
private void parseKeyTypeAttr(FieldMetaData fmd, Attributes attrs)
throws SAXException {
String typeStr = attrs.getValue("key-type");
if (!StringUtil.isEmpty(typeStr)) {
if (StringUtil.endsWithIgnoreCase(typeStr, ".class")) {
typeStr = typeStr.substring(0, typeStr.lastIndexOf('.'));
}
Class<?> typeCls = parseTypeStr(typeStr);
fmd.setTypeOverride(typeCls);
}
}
private Class<?> parseTypeStr(String typeStr)
throws SAXException {
Class<?> typeCls = null;
try {
if (typeStr.equalsIgnoreCase("int")) {
typeCls = int.class;
}
else if (typeStr.equalsIgnoreCase("byte")) {
typeCls = byte.class;
}
else if (typeStr.equalsIgnoreCase("short")) {
typeCls = short.class;
}
else if (typeStr.equalsIgnoreCase("long")) {
typeCls = long.class;
}
else if (typeStr.equalsIgnoreCase("float")) {
typeCls = float.class;
}
else if (typeStr.equalsIgnoreCase("double")) {
typeCls = double.class;
}
else if (typeStr.equalsIgnoreCase("boolean")) {
typeCls = boolean.class;
}
else if (typeStr.equalsIgnoreCase("char")) {
typeCls = char.class;
}
else {
typeCls = Class.forName(typeStr);
}
} catch (ClassNotFoundException e) {
throw new SAXException(e);
}
return typeCls;
}
private void parseInverseLogicalAttr(FieldMetaData fmd, Attributes attrs)
throws SAXException {
String inverseLogical = attrs.getValue("inverse-logical");
if (!StringUtil.isEmpty(inverseLogical)) {
fmd.setInverse(inverseLogical);
}
}
protected void parseEagerFetchModeAttr(FieldMetaData fmd, Attributes attrs)
throws SAXException {
}
private boolean startExternalValues(Attributes attrs)
throws SAXException {
_externalValues = new StringBuffer(10);
return true;
}
private void endExternalValues()
throws SAXException {
FieldMetaData fmd = (FieldMetaData) currentElement();
fmd.setExternalValues(_externalValues.toString());
_externalValues = null;
}
private boolean startExternalValue(Attributes attrs)
throws SAXException {
if (_externalValues.length() > 0) {
_externalValues.append(',');
}
_externalValues.append(attrs.getValue("java-value"));
_externalValues.append('=');
_externalValues.append(attrs.getValue("datastore-value"));
return true;
}
private boolean startExternalizer(Attributes attrs)
throws SAXException {
return true;
}
private void endExternalizer()
throws SAXException {
FieldMetaData fmd = (FieldMetaData) currentElement();
String externalizer = currentText();
fmd.setExternalizer(externalizer);
}
private boolean startFactory(Attributes attrs)
throws SAXException {
return true;
}
private void endFactory()
throws SAXException {
FieldMetaData fmd = (FieldMetaData) currentElement();
String factory = currentText();
fmd.setFactory(factory);
}
private boolean startFetchGroups(Attributes attrs)
throws SAXException {
if (_fgList == null) {
_fgList = new ArrayList<>();
}
return true;
}
private boolean startFetchGroup(Attributes attrs)
throws SAXException {
if (_fgList == null) {
_fgList = new ArrayList<>();
}
_currentFg = new AnnotationPersistenceMetaDataParser.FetchGroupImpl(attrs.getValue("name"),
Boolean.parseBoolean(attrs.getValue("post-load")));
return true;
}
private void endFetchGroup()
throws SAXException {
String[] referencedFetchGroups = {};
if (_referencedFgList != null &&_referencedFgList.size() > 0) {
referencedFetchGroups = _referencedFgList.toArray(referencedFetchGroups);
}
_currentFg.setFetchGroups(referencedFetchGroups);
FetchAttributeImpl[] fetchAttrs = {};
if (_fetchAttrList != null && _fetchAttrList.size() > 0) {
fetchAttrs = _fetchAttrList.toArray(fetchAttrs);
}
_currentFg.setAttributes(fetchAttrs);
_fgList.add(_currentFg);
_currentFg = null;
_referencedFgList = null;
_fetchAttrList = null;
}
private boolean startFetchAttribute(Attributes attrs)
throws SAXException {
if (_fetchAttrList == null) {
_fetchAttrList = new ArrayList<>();
}
FetchAttributeImpl fetchAttribute = new FetchAttributeImpl(attrs.getValue("name"),
Integer.parseInt(attrs.getValue("recursion-depth")));
_fetchAttrList.add(fetchAttribute);
return true;
}
private boolean startReferencedFetchGroup(Attributes attrs)
throws SAXException {
if (_referencedFgList == null) {
_referencedFgList = new ArrayList<>();
}
return true;
}
private void endReferencedFetchGroup()
throws SAXException {
_referencedFgList.add(currentText());
}
@Override
protected void endExtendedClass(String elem) throws SAXException {
ClassMetaData meta = (ClassMetaData) peekElement();
if (_fgList != null) {
// Handle fetch groups
_fgs = new FetchGroupImpl[]{};
_fgs = _fgList.toArray(_fgs);
AnnotationPersistenceMetaDataParser.parseFetchGroups(meta, _fgs);
_fgList = null;
_fgs = null;
}
}
}