blob: e06227057ee8b5fbe4dd3d714bbcfc8de76f9225 [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 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 java.io.File;
import java.lang.reflect.Field;
import java.lang.reflect.Member;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Properties;
import org.apache.openjpa.conf.OpenJPAConfiguration;
import org.apache.openjpa.kernel.QueryLanguages;
import org.apache.openjpa.lib.conf.Configurations;
import org.apache.openjpa.lib.log.Log;
import org.apache.openjpa.lib.meta.CFMetaDataSerializer;
import org.apache.openjpa.lib.meta.SourceTracker;
import org.apache.openjpa.lib.util.ClassUtil;
import org.apache.openjpa.lib.util.JavaVersions;
import org.apache.openjpa.lib.util.Localizer;
import org.apache.openjpa.meta.AccessCode;
import org.apache.openjpa.meta.ClassMetaData;
import org.apache.openjpa.meta.FieldMetaData;
import org.apache.openjpa.meta.JavaTypes;
import org.apache.openjpa.meta.MetaDataInheritanceComparator;
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 org.apache.openjpa.util.InternalException;
import org.xml.sax.SAXException;
/**
* Serializes persistence metadata back to XML.
* This class processes all object level tags that are store-agnostic.
* However, it provides hooks for the subclasses to include store-specific
* tags to be serialized both at <entity-mappings> and
* <entity> level.
*
* @since 0.4.0
* @author Steve Kim
*/
public class XMLPersistenceMetaDataSerializer
extends CFMetaDataSerializer
implements PersistenceMetaDataFactory.Serializer {
// NOTE: order is important! these constants must be maintained in
// serialization order. constants are spaced so that subclasses can
// slip tags in-between
protected static final int TYPE_SEQ = 10;
protected static final int TYPE_QUERY = 20;
protected static final int TYPE_META = 30;
protected static final int TYPE_CLASS_SEQS = 40;
protected static final int TYPE_CLASS_QUERIES = 50;
private static final Localizer _loc = Localizer.forPackage
(XMLPersistenceMetaDataSerializer.class);
private final OpenJPAConfiguration _conf;
private Map<String, ClassMetaData> _metas = null;
private Map<String, List<QueryMetaData>> _queries = null;
private Map<String, List<SequenceMetaData>> _seqs = null;
private int _mode = MODE_NONE;
private boolean _annos = true;
private SerializationComparator _comp = null;
/**
* Constructor. Supply configuration.
*/
public XMLPersistenceMetaDataSerializer(OpenJPAConfiguration conf) {
_conf = conf;
setLog(conf.getLog(OpenJPAConfiguration.LOG_METADATA));
setMode(MODE_META | MODE_MAPPING | MODE_QUERY);
}
/**
* Configuration.
*/
public OpenJPAConfiguration getConfiguration() {
return _conf;
}
/**
* Whether to serialize content originally specified in annotations.
* Defaults to true.
*/
public boolean getSerializeAnnotations() {
return _annos;
}
/**
* Whether to serialize content originally specified in annotations.
* Defaults to true.
*/
public void setSerializeAnnotations(boolean annos) {
_annos = annos;
}
/**
* The serialization 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 serialization mode according to the expected document type. The
* mode constants act as bit flags, and therefore can be combined.
*/
@Override
public void setMode(int mode) {
_mode = mode;
}
/**
* The serialization 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);
}
/**
* Override to not overwrite annotations.
*/
@Override
protected File getSourceFile(Object obj) {
File file = super.getSourceFile(obj);
if (file == null || file.getName().endsWith(".java")
|| file.getName().endsWith(".class"))
return null;
return 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;
}
/**
* Convenience method for interpreting {@link #getMode}. Takes into
* account whether mapping information is loaded for the given instance.
*/
protected boolean isMappingMode(ClassMetaData meta) {
return isMappingMode() && (meta.getSourceMode() & MODE_MAPPING) != 0
&& (meta.getEmbeddingMetaData() != null
|| !meta.isEmbeddedOnly())
&& (meta.getEmbeddingMetaData() == null
|| isMappingMode(meta.getEmbeddingMetaData()));
}
/**
* Convenience method for interpreting {@link #getMode}. Takes into
* account whether mapping information is loaded for the given instance.
*/
protected boolean isMappingMode(ValueMetaData vmd) {
return isMappingMode(vmd.getFieldMetaData().getDefiningMetaData());
}
/**
* Add a class meta data to the set to be serialized.
*/
@Override
public void addMetaData(ClassMetaData meta) {
if (meta == null)
return;
if (_metas == null)
_metas = new HashMap<>();
_metas.put(meta.getDescribedType().getName(), meta);
}
/**
* Add a sequence meta data to the set to be serialized.
*/
@Override
public void addSequenceMetaData(SequenceMetaData meta) {
if (meta == null)
return;
List<SequenceMetaData> seqs = null;
String defName = null;
if (meta.getSourceScope() instanceof Class)
defName = ((Class) meta.getSourceScope()).getName();
if (_seqs == null)
_seqs = new HashMap<>();
else
seqs = _seqs.get(defName);
if (seqs == null) {
seqs = new ArrayList<>(3); // don't expect many seqs / class
seqs.add(meta);
_seqs.put(defName, seqs);
} else if (!seqs.contains(meta))
seqs.add(meta);
}
/**
* Add a query meta data to the set to be serialized.
*/
@Override
public void addQueryMetaData(QueryMetaData meta) {
if (meta == null)
return;
List<QueryMetaData> queries = null;
String defName = null;
if (meta.getSourceScope() instanceof Class)
defName = ((Class) meta.getSourceScope()).getName();
if (_queries == null)
_queries = new HashMap<>();
else
queries = _queries.get(defName);
if (queries == null) {
queries = new ArrayList(3); // don't expect many queries / class
queries.add(meta);
_queries.put(defName, queries);
} else if (!queries.contains(meta))
queries.add(meta);
}
/**
* Add all components in the given repository to the set to be serialized.
*/
@Override
public void addAll(MetaDataRepository repos) {
if (repos == null)
return;
for (ClassMetaData meta : repos.getMetaDatas())
addMetaData(meta);
for (SequenceMetaData seq : repos.getSequenceMetaDatas())
addSequenceMetaData(seq);
for (QueryMetaData query : repos.getQueryMetaDatas())
addQueryMetaData(query);
}
/**
* Remove a metadata from the set to be serialized.
*
* @return true if removed, false if not in set
*/
@Override
public boolean removeMetaData(ClassMetaData meta) {
return _metas != null && meta != null
&& _metas.remove(meta.getDescribedType().getName()) != null;
}
/**
* Remove a sequence metadata from the set to be serialized.
*
* @return true if removed, false if not in set
*/
public boolean removeSequenceMetaData(SequenceMetaData meta) {
if (_seqs == null || meta == null)
return false;
String defName = null;
if (meta.getSourceScope() instanceof Class)
defName = ((Class) meta.getSourceScope()).getName();
List<SequenceMetaData> seqs = _seqs.get(defName);
if (seqs == null)
return false;
if (!seqs.remove(meta))
return false;
if (seqs.isEmpty())
_seqs.remove(defName);
return true;
}
/**
* Remove a query metadata from the set to be serialized.
*
* @return true if removed, false if not in set
*/
public boolean removeQueryMetaData(QueryMetaData meta) {
if (_queries == null || meta == null)
return false;
String defName = null;
if (meta.getSourceScope() instanceof Class)
defName = ((Class) meta.getSourceScope()).getName();
List<QueryMetaData> queries = _queries.get(defName);
if (queries == null)
return false;
if (!queries.remove(meta))
return false;
if (queries.isEmpty())
_queries.remove(defName);
return true;
}
/**
* Remove all the components in the given repository from the set to be
* serialized.
*
* @return true if any components removed, false if none in set
*/
public boolean removeAll(MetaDataRepository repos) {
if (repos == null)
return false;
boolean removed = false;
ClassMetaData[] metas = repos.getMetaDatas();
for (ClassMetaData meta : metas) {
removed |= removeMetaData(meta);
}
SequenceMetaData[] seqs = repos.getSequenceMetaDatas();
for (SequenceMetaData seq : seqs) {
removed |= removeSequenceMetaData(seq);
}
QueryMetaData[] queries = repos.getQueryMetaDatas();
for (QueryMetaData query : queries) {
removed |= removeQueryMetaData(query);
}
return removed;
}
/**
* Clear the set of metadatas to be serialized.
*/
public void clear() {
if (_metas != null)
_metas.clear();
if (_seqs != null)
_seqs.clear();
if (_queries != null)
_queries.clear();
}
@Override
protected Collection getObjects() {
List all = new ArrayList();
if (isQueryMode())
addQueryMetaDatas(all);
if (isMappingMode())
addSequenceMetaDatas(all);
if ((isMetaDataMode() || isMappingMode()) && _metas != null)
all.addAll(_metas.values());
if (isMappingMode())
addSystemMappingElements(all);
serializationSort(all);
return all;
}
/**
* Add system-level mapping elements to be serialized. Does nothing
* by default.
*/
protected void addSystemMappingElements(Collection toSerialize) {
}
/**
* Sort the given collection of objects to be serialized.
*/
private void serializationSort(List objs) {
if (objs == null || objs.isEmpty())
return;
if (_comp == null)
_comp = newSerializationComparator();
Collections.sort(objs, _comp);
}
/**
* Create a new comparator for ordering objects that are to be serialized.
*/
protected SerializationComparator newSerializationComparator() {
return _comp;
}
/**
* Add sequence metadata to the given metadatas collection.
*/
private void addSequenceMetaDatas(Collection all) {
if (_seqs == null)
return;
for (Map.Entry entry : _seqs.entrySet()) {
if (entry.getKey() == null)
all.addAll((List) entry.getValue());
else if (_metas == null || !_metas.containsKey(entry.getKey()))
all.add(new ClassSeqs((List<SequenceMetaData>)
entry.getValue()));
}
}
/**
* Add query metadata to the given metadatas collection.
*/
private void addQueryMetaDatas(Collection all) {
if (_queries == null)
return;
for (Map.Entry entry : _queries.entrySet()) {
if (entry.getKey() == null)
all.addAll((List) entry.getValue());
else if (_mode == MODE_QUERY || _metas == null
|| !_metas.containsKey(entry.getKey()))
all.add(new ClassQueries((List<QueryMetaData>)
entry.getValue()));
}
}
@Override
protected void serialize(Collection objects)
throws SAXException {
// copy collection to avoid mutation
Object meta;
boolean unique = true;
boolean fieldAccess = false;
boolean propertyAccess = false;
for (Object object : objects) {
meta = object;
switch (type(meta)) {
case TYPE_META:
ClassMetaData cls = (ClassMetaData) meta;
if (AccessCode.isField(cls.getAccessType()))
fieldAccess = true;
else
propertyAccess = true;
// no break
default:
if (unique && getPackage() == null)
setPackage(getPackage(meta));
else if (unique) {
unique = getPackage().equals(getPackage(meta));
if (!unique)
setPackage(null);
}
}
}
serializeNamespaceAttributes();
startElement("entity-mappings");
if (getPackage() != null) {
startElement("package");
addText(getPackage());
endElement("package");
}
if (fieldAccess != propertyAccess) // i.e. only one
{
int def = getConfiguration().getMetaDataRepositoryInstance().
getMetaDataFactory().getDefaults().getDefaultAccessType();
String access = null;
if (fieldAccess && AccessCode.isProperty(def))
access = "FIELD";
else if (propertyAccess && AccessCode.isField(def))
access = "PROPERTY";
if (access != null) {
startElement("access");
addText(access);
endElement("access");
}
}
for (Object obj : objects) {
int type = type(obj);
switch (type) {
case TYPE_META:
serializeClass((ClassMetaData) obj, fieldAccess
&& propertyAccess);
break;
case TYPE_SEQ:
if (isMappingMode())
serializeSequence((SequenceMetaData) obj);
break;
case TYPE_QUERY:
serializeQuery((QueryMetaData) obj);
break;
case TYPE_CLASS_QUERIES:
for (QueryMetaData query : ((ClassQueries) obj).getQueries())
serializeQuery(query);
break;
case TYPE_CLASS_SEQS:
if (isMappingMode())
for (SequenceMetaData seq : ((ClassSeqs) obj).getSequences())
serializeSequence(seq);
break;
default:
if (isMappingMode())
serializeSystemMappingElement(obj);
break;
}
}
endElement("entity-mappings");
}
@Override
protected String getPackage(Object obj) {
int type = type(obj);
switch (type) {
case TYPE_META:
return ClassUtil.getPackageName(((ClassMetaData) obj).
getDescribedType());
case TYPE_QUERY:
case TYPE_SEQ:
case TYPE_CLASS_QUERIES:
case TYPE_CLASS_SEQS:
SourceTracker st = (SourceTracker) obj;
if (st.getSourceScope() instanceof Class)
return ClassUtil.getPackageName((Class) st.getSourceScope());
return null;
default:
return null;
}
}
/**
* Return the type constant for the given object based on its runtime
* class. If the runtime class does not correspond to any of the known
* types then returns -1. This can happen for tags
* that are not handled at this store-agnostic level.
*/
protected int type(Object o) {
if (o instanceof ClassMetaData)
return TYPE_META;
if (o instanceof QueryMetaData)
return TYPE_QUERY;
if (o instanceof SequenceMetaData)
return TYPE_SEQ;
if (o instanceof ClassQueries)
return TYPE_CLASS_QUERIES;
if (o instanceof ClassSeqs)
return TYPE_CLASS_SEQS;
return -1;
}
/**
* Serialize namespace attributes
*/
private void serializeNamespaceAttributes()
throws SAXException {
addAttribute("xmlns", "http://java.sun.com/xml/ns/persistence/orm");
addAttribute("xmlns:xsi", "http://www.w3.org/2001/XMLSchema-instance");
addAttribute("xsi:schemaLocation",
"http://java.sun.com/xml/ns/persistence/orm orm_2_0.xsd");
addAttribute("version", "2.0");
}
/**
* Serialize unknown mapping element at system level.
*/
protected void serializeSystemMappingElement(Object obj)
throws SAXException {
}
/**
* Serialize query metadata.
*/
private void serializeQuery(QueryMetaData meta)
throws SAXException {
if (!_annos && meta.getSourceType() == SourceTracker.SRC_ANNOTATIONS)
return;
Log log = getLog();
if (log.isInfoEnabled()) {
if (meta.getSourceScope() instanceof Class)
log.info(_loc.get("ser-cls-query",
meta.getSourceScope(), meta.getName()));
else
log.info(_loc.get("ser-query", meta.getName()));
}
addComments(meta);
addAttribute("name", meta.getName());
addAttribute("query", meta.getQueryString());
if (QueryLanguages.LANG_SQL.equals(meta.getLanguage())) {
if (meta.getResultType() != null)
addAttribute("result-class", meta.getResultType().getName());
startElement("named-native-query");
serializeQueryHints(meta);
endElement("named-native-query");
} else {
startElement("named-query");
serializeQueryHints(meta);
endElement("named-query");
}
}
/**
* Serialize query hints.
*/
private void serializeQueryHints(QueryMetaData meta)
throws SAXException {
String[] hints = meta.getHintKeys();
Object[] values = meta.getHintValues();
for (int i = 0; i < hints.length; i++) {
addAttribute("name", hints[i]);
addAttribute("value", String.valueOf(values[i]));
startElement("query-hint");
endElement("query-hint");
}
}
/**
* Serialize sequence metadata.
*/
protected void serializeSequence(SequenceMetaData meta)
throws SAXException {
if (!_annos && meta.getSourceType() == SourceTracker.SRC_ANNOTATIONS)
return;
Log log = getLog();
if (log.isInfoEnabled())
log.info(_loc.get("ser-sequence", meta.getName()));
addComments(meta);
addAttribute("name", meta.getName());
// parse out the datastore sequence name, if any
String plugin = meta.getSequencePlugin();
String clsName = Configurations.getClassName(plugin);
String props = Configurations.getProperties(plugin);
String ds = null;
if (props != null) {
Properties map = Configurations.parseProperties(props);
ds = (String) map.remove("Sequence");
if (ds != null) {
props = Configurations.serializeProperties(map);
plugin = Configurations.getPlugin(clsName, props);
}
}
if (ds != null)
addAttribute("sequence-name", ds);
else if (plugin != null && !SequenceMetaData.IMPL_NATIVE.equals
(plugin))
addAttribute("sequence-name", plugin);
if (meta.getInitialValue() != 0 && meta.getInitialValue() != -1)
addAttribute("initial-value",
String.valueOf(meta.getInitialValue()));
if (meta.getAllocate() != 50 && meta.getAllocate() != -1)
addAttribute("allocation-size",
String.valueOf(meta.getAllocate()));
startElement("sequence-generator");
endElement("sequence-generator");
}
/**
* Serialize class metadata.
*/
protected void serializeClass(ClassMetaData meta, boolean access)
throws SAXException {
if (!_annos && meta.getSourceType() == SourceTracker.SRC_ANNOTATIONS)
return;
Log log = getLog();
if (log.isInfoEnabled())
log.info(_loc.get("ser-class", meta));
addComments(meta);
addAttribute("class", getClassName(meta.getDescribedType().
getName()));
if (isMetaDataMode()
&& !meta.getTypeAlias().equals(ClassUtil.getClassName(meta.
getDescribedType())))
addAttribute("name", meta.getTypeAlias());
String name = getEntityElementName(meta);
if (isMetaDataMode())
addClassAttributes(meta, access);
if (isMappingMode())
addClassMappingAttributes(meta);
startElement(name);
if (isMappingMode())
serializeClassMappingContent(meta);
if (isMetaDataMode())
serializeIdClass(meta);
if (isMappingMode())
serializeInheritanceContent(meta);
if (isMappingMode()) {
List<SequenceMetaData> seqs = (_seqs == null) ? null : _seqs.get
(meta.getDescribedType().getName());
if (seqs != null) {
serializationSort(seqs);
for (Object seq : seqs) {
serializeSequence((SequenceMetaData) seq);
}
}
}
if (isQueryMode()) {
List queries = (_queries == null) ? null : _queries.get
(meta.getDescribedType().getName());
if (queries != null) {
serializationSort(queries);
for (Object query : queries) {
serializeQuery((QueryMetaData) query);
}
}
if (isMappingMode())
serializeQueryMappings(meta);
}
List<FieldMetaData> fields = new ArrayList<>(Arrays.asList
(meta.getDefinedFieldsInListingOrder()));
Collections.sort(fields, new FieldComparator());
// serialize attr-override
if (isMappingMode()) {
FieldMetaData fmd;
FieldMetaData orig;
for (Iterator<FieldMetaData> it = fields.iterator(); it.hasNext();)
{
fmd = it.next();
if (meta.getDefinedSuperclassField(fmd.getName()) == null)
continue;
orig = meta.getPCSuperclassMetaData().getField(fmd.getName());
if (serializeAttributeOverride(fmd, orig))
serializeAttributeOverrideContent(fmd, orig);
it.remove();
}
}
if (fields.size() > 0 && (isMetaDataMode() || isMappingMode())) {
startElement("attributes");
FieldMetaData orig;
for (FieldMetaData fmd : fields) {
if (fmd.getDeclaringType() != fmd.getDefiningMetaData().
getDescribedType()) {
orig = fmd.getDeclaringMetaData().getDeclaredField
(fmd.getName());
} else
orig = null;
serializeField(fmd, orig);
}
endElement("attributes");
}
endElement(name);
}
/**
* Return the entity element name.
*/
private static String getEntityElementName(ClassMetaData meta) {
switch (getEntityTag(meta)) {
case ENTITY:
return "entity";
case EMBEDDABLE:
return "embeddable";
case MAPPED_SUPERCLASS:
return "mapped-superclass";
default:
throw new IllegalStateException();
}
}
/**
* Return the MetaDataTag for the given class meta data.
*/
private static MetaDataTag getEntityTag(ClassMetaData meta) {
// @Embeddable classes can't declare Id fields
if (meta.isEmbeddedOnly() && meta.getPrimaryKeyFields().length == 0)
return MetaDataTag.EMBEDDABLE;
if (meta.isMapped())
return MetaDataTag.ENTITY;
return MetaDataTag.MAPPED_SUPERCLASS;
}
/**
* Set class attributes.
*
* @param access whether to write access
*/
private void addClassAttributes(ClassMetaData meta, boolean access) {
if (!access)
return;
int def = getConfiguration().getMetaDataRepositoryInstance().
getMetaDataFactory().getDefaults().getDefaultAccessType();
if (AccessCode.isField(meta.getAccessType())
&& AccessCode.isProperty(def))
addAttribute("access", "FIELD");
else if (AccessCode.isProperty(meta.getAccessType())
&& AccessCode.isField(def))
addAttribute("access", "PROPERTY");
}
/**
* Add mapping attributes for the given class. Does nothing by default
*/
protected void addClassMappingAttributes(ClassMetaData mapping)
throws SAXException {
}
/**
* Serialize id-class.
*/
private void serializeIdClass(ClassMetaData meta)
throws SAXException {
if (meta.getIdentityType() != ClassMetaData.ID_APPLICATION
|| meta.isOpenJPAIdentity())
return;
ClassMetaData sup = meta.getPCSuperclassMetaData();
Class<?> oid = meta.getObjectIdType();
if (oid != null && (sup == null || oid != sup.getObjectIdType())) {
addAttribute("class", getClassName(oid.getName()));
startElement("id-class");
endElement("id-class");
}
}
/**
* Serialize class mapping content. Does nothing by default.
*/
protected void serializeClassMappingContent(ClassMetaData mapping)
throws SAXException {
}
/**
* Serialize inheritance content. Does nothing by default.
*/
protected void serializeInheritanceContent(ClassMetaData mapping)
throws SAXException {
}
/**
* Serialize query mappings. Does nothing by default.
*/
protected void serializeQueryMappings(ClassMetaData meta)
throws SAXException {
}
/**
* Serialize the given field.
*/
private void serializeField(FieldMetaData fmd, FieldMetaData orig)
throws SAXException {
if (fmd.getManagement() != FieldMetaData.MANAGE_PERSISTENT
&& !fmd.isExplicit())
return;
addComments(fmd);
addAttribute("name", fmd.getName());
String strategy = null;
PersistenceStrategy strat = getStrategy(fmd);
ValueMetaData cascades = null;
if (fmd.isPrimaryKey() && strat == PersistenceStrategy.EMBEDDED)
strategy = "embedded-id";
else if (fmd.isPrimaryKey())
strategy = "id";
else if (fmd.isVersion())
strategy = "version";
else {
switch (strat) {
case TRANSIENT:
strategy = "transient";
break;
case BASIC:
if (isMetaDataMode())
addBasicAttributes(fmd);
strategy = "basic";
break;
case EMBEDDED:
strategy = "embedded";
break;
case MANY_ONE:
if (isMetaDataMode())
addManyToOneAttributes(fmd);
strategy = "many-to-one";
cascades = fmd;
break;
case ONE_ONE:
if (isMetaDataMode())
addOneToOneAttributes(fmd);
strategy = "one-to-one";
cascades = fmd;
break;
case ONE_MANY:
if (isMetaDataMode())
addOneToManyAttributes(fmd);
strategy = "one-to-many";
cascades = fmd.getElement();
break;
case MANY_MANY:
if (isMetaDataMode())
addManyToManyAttributes(fmd);
strategy = "many-to-many";
cascades = fmd.getElement();
break;
case ELEM_COLL:
if (isMetaDataMode())
addElementCollectionAttributes(fmd);
strategy = "element-collection";
break;
}
if (isMappingMode())
addStrategyMappingAttributes(fmd);
}
if (isMappingMode(fmd))
addFieldMappingAttributes(fmd, orig);
startElement(strategy);
if (fmd.getOrderDeclaration() != null) {
startElement("order-by");
if (!(Order.ELEMENT + " asc").equals(fmd.getOrderDeclaration()))
addText(fmd.getOrderDeclaration());
endElement("order-by");
} else if (isMappingMode(fmd)) {
serializeOrderColumn(fmd);
}
if (isMappingMode() && fmd.getKey().getValueMappedBy() != null) {
FieldMetaData mapBy = fmd.getKey().getValueMappedByMetaData();
if (!mapBy.isPrimaryKey() ||
mapBy.getDefiningMetaData().getPrimaryKeyFields().length != 1) {
addAttribute("name", fmd.getKey().getValueMappedBy());
}
startElement("map-key");
endElement("map-key");
}
if (isMappingMode(fmd))
serializeFieldMappingContent(fmd, strat);
if (cascades != null && isMetaDataMode())
serializeCascades(cascades);
if (isMappingMode() && strat == PersistenceStrategy.EMBEDDED) {
ClassMetaData meta = fmd.getEmbeddedMetaData();
ClassMetaData owner = getConfiguration().
getMetaDataRepositoryInstance().getMetaData
(meta.getDescribedType(), meta.getEnvClassLoader(), true);
FieldMetaData eorig;
for (FieldMetaData efmd : meta.getFields()) {
eorig = owner.getField(efmd.getName());
if (serializeAttributeOverride(efmd, eorig))
serializeAttributeOverrideContent(efmd, eorig);
}
}
endElement(strategy);
}
/**
* Add mapping attributes for the given field. Does nothing by default.
*/
protected void addFieldMappingAttributes(FieldMetaData fmd,
FieldMetaData orig)
throws SAXException {
}
/**
* Always returns false by default.
*/
protected boolean serializeAttributeOverride(FieldMetaData fmd,
FieldMetaData orig) {
return false;
}
/**
* Serialize attribute override content.
*/
private void serializeAttributeOverrideContent(FieldMetaData fmd,
FieldMetaData orig)
throws SAXException {
addAttribute("name", fmd.getName());
startElement("attribute-override");
serializeAttributeOverrideMappingContent(fmd, orig);
endElement("attribute-override");
}
/**
* Serialize attribute override mapping content. Does nothing by default,
*/
protected void serializeAttributeOverrideMappingContent
(FieldMetaData fmd, FieldMetaData orig)
throws SAXException {
}
/**
* Serialize cascades.
*/
private void serializeCascades(ValueMetaData vmd)
throws SAXException {
Collection<String> cascades = null;
if (vmd.getCascadePersist() == ValueMetaData.CASCADE_IMMEDIATE) {
if (cascades == null)
cascades = new ArrayList<>();
cascades.add("cascade-persist");
}
if (vmd.getCascadeAttach() == ValueMetaData.CASCADE_IMMEDIATE) {
if (cascades == null)
cascades = new ArrayList<>();
cascades.add("cascade-merge");
}
if (vmd.getCascadeDelete() == ValueMetaData.CASCADE_IMMEDIATE) {
if (cascades == null)
cascades = new ArrayList<>();
cascades.add("cascade-remove");
}
if (vmd.getCascadeRefresh() == ValueMetaData.CASCADE_IMMEDIATE) {
if (cascades == null)
cascades = new ArrayList<>();
cascades.add("cascade-refresh");
}
if (vmd.getCascadeDetach() == ValueMetaData.CASCADE_IMMEDIATE) {
if (cascades == null)
cascades = new ArrayList<>();
cascades.add("cascade-detach");
}
if (cascades != null && cascades.size() == 5) // ALL
{
cascades.clear();
cascades.add("cascade-all");
}
if (cascades != null) {
startElement("cascade");
for (String cascade : cascades) {
startElement(cascade);
endElement(cascade);
}
endElement("cascade");
}
}
/**
* Return the serialized strategy name.
*/
protected PersistenceStrategy getStrategy(FieldMetaData fmd) {
if (fmd.getManagement() == FieldMetaData.MANAGE_NONE)
return PersistenceStrategy.TRANSIENT;
if (fmd.isSerialized()
|| fmd.getDeclaredType() == byte[].class
|| fmd.getDeclaredType() == Byte[].class
|| fmd.getDeclaredType() == char[].class
|| fmd.getDeclaredType() == Character[].class)
return PersistenceStrategy.BASIC;
FieldMetaData mappedBy;
switch (fmd.getDeclaredTypeCode()) {
case JavaTypes.PC:
if (fmd.isEmbedded())
return PersistenceStrategy.EMBEDDED;
if (fmd.getMappedBy() != null)
return PersistenceStrategy.ONE_ONE;
FieldMetaData[] inverses = fmd.getInverseMetaDatas();
if (inverses.length == 1 &&
inverses[0].getTypeCode() == JavaTypes.PC &&
inverses[0].getMappedByMetaData() == fmd) {
return PersistenceStrategy.ONE_ONE;
}
return PersistenceStrategy.MANY_ONE;
case JavaTypes.ARRAY:
case JavaTypes.COLLECTION:
case JavaTypes.MAP:
if (fmd.isElementCollection())
return PersistenceStrategy.ELEM_COLL;
mappedBy = fmd.getMappedByMetaData();
if (mappedBy == null || mappedBy.getTypeCode() != JavaTypes.PC)
return PersistenceStrategy.MANY_MANY;
return PersistenceStrategy.ONE_MANY;
case JavaTypes.OID:
return PersistenceStrategy.EMBEDDED;
default:
return PersistenceStrategy.BASIC;
}
}
/**
* Add basic attributes.
*/
private void addBasicAttributes(FieldMetaData fmd)
throws SAXException {
if (!fmd.isInDefaultFetchGroup())
addAttribute("fetch", "LAZY");
if (fmd.getNullValue() == FieldMetaData.NULL_EXCEPTION)
addAttribute("optional", "false");
}
/**
* Add many-to-one attributes.
*/
private void addManyToOneAttributes(FieldMetaData fmd)
throws SAXException {
if (!fmd.isInDefaultFetchGroup())
addAttribute("fetch", "LAZY");
if (fmd.getNullValue() == FieldMetaData.NULL_EXCEPTION)
addAttribute("optional", "false");
}
/**
* Add one-to-one attributes.
*/
private void addOneToOneAttributes(FieldMetaData fmd)
throws SAXException {
if (!fmd.isInDefaultFetchGroup())
addAttribute("fetch", "LAZY");
if (fmd.getNullValue() == FieldMetaData.NULL_EXCEPTION)
addAttribute("optional", "false");
}
/**
* Add one-to-many attributes.
*/
private void addOneToManyAttributes(FieldMetaData fmd)
throws SAXException {
if (fmd.isInDefaultFetchGroup())
addAttribute("fetch", "EAGER");
addTargetEntityAttribute(fmd);
}
/**
* Add many-to-many attributes.
*/
private void addManyToManyAttributes(FieldMetaData fmd)
throws SAXException {
if (fmd.isInDefaultFetchGroup())
addAttribute("fetch", "EAGER");
addTargetEntityAttribute(fmd);
}
/**
* Add element-collection attributes.
*/
private void addElementCollectionAttributes(FieldMetaData fmd)
throws SAXException {
if (fmd.isInDefaultFetchGroup())
addAttribute("fetch", "EAGER");
addTargetEntityAttribute(fmd);
}
/**
* Add a target-entity attribute to collection and map fields that do
* not use generics.
*/
private void addTargetEntityAttribute(FieldMetaData fmd)
throws SAXException {
Member member = fmd.getBackingMember();
Class[] types;
if (member instanceof Field)
types = JavaVersions.getParameterizedTypes((Field) member);
else if (member instanceof Method)
types = JavaVersions.getParameterizedTypes((Method) member);
else
types = new Class[0];
switch (fmd.getDeclaredTypeCode()) {
case JavaTypes.COLLECTION:
if (types.length != 1)
addAttribute("target-entity", fmd.getElement().
getDeclaredType().getName());
break;
case JavaTypes.MAP:
if (types.length != 2)
addAttribute("target-entity", fmd.getElement().
getDeclaredType().getName());
break;
}
}
/**
* Serialize field mapping content; this will be called before
* {@link #serializeValueMappingContent}. Does nothing by default.
*/
protected void serializeFieldMappingContent(FieldMetaData fmd,
PersistenceStrategy strategy)
throws SAXException {
}
/**
* Set mapping attributes for strategy. Sets mapped-by by default.
*/
protected void addStrategyMappingAttributes(FieldMetaData fmd)
throws SAXException {
if (fmd.getMappedBy() != null)
addAttribute("mapped-by", fmd.getMappedBy());
}
/**
* Order column is not processed as meta data, instead it
* can be processed as mapping data if in mapping mode.
*/
protected void serializeOrderColumn(FieldMetaData fmd)
throws SAXException {
}
/**
* Represents ordered set of {@link SequenceMetaData}s with a
* common class scope.
*
* @author Stephen Kim
* @author Pinaki Poddar
*/
private static class ClassSeqs
implements SourceTracker, Comparable<ClassSeqs>,
Comparator<SequenceMetaData> {
private final SequenceMetaData[] _seqs;
public ClassSeqs(List<SequenceMetaData> seqs) {
if (seqs == null || seqs.isEmpty())
throw new InternalException();
_seqs = (SequenceMetaData[]) seqs.toArray
(new SequenceMetaData[seqs.size()]);
Arrays.sort(_seqs, this);
}
public SequenceMetaData[] getSequences() {
return _seqs;
}
/**
* Compare sequence metadata on name.
*/
@Override
public int compare(SequenceMetaData o1, SequenceMetaData o2) {
return o1.getName().compareTo(o2.getName());
}
@Override
public File getSourceFile() {
return _seqs[0].getSourceFile();
}
@Override
public Object getSourceScope() {
return _seqs[0].getSourceScope();
}
@Override
public int getSourceType() {
return _seqs[0].getSourceType();
}
@Override
public String getResourceName() {
return _seqs[0].getResourceName();
}
@Override
public int getLineNumber() {
return _seqs[0].getLineNumber();
}
@Override
public int getColNumber() {
return _seqs[0].getColNumber();
}
@Override
public int compareTo(ClassSeqs other) {
if (other == this)
return 0;
if (other == null)
return -1;
Class scope = (Class) getSourceScope();
Class oscope = (Class) other.getSourceScope();
return scope.getName().compareTo(oscope.getName());
}
}
/**
* Represents ordered set of {@link QueryMetaData}s with a
* common class scope.
*
* @author Stephen Kim
* @author Pinaki Poddar
*/
private static class ClassQueries
implements SourceTracker, Comparable<ClassQueries>,
Comparator<QueryMetaData> {
private final QueryMetaData[] _queries;
public ClassQueries(List<QueryMetaData> queries) {
if (queries == null || queries.isEmpty())
throw new InternalException();
_queries = (QueryMetaData[]) queries.toArray
(new QueryMetaData[queries.size()]);
Arrays.sort(_queries, this);
}
public QueryMetaData[] getQueries() {
return _queries;
}
/**
* Compare query metadata. Normal queries appear before native queries.
* If the given queries use same language, then their names are
* compared.
*/
@Override
public int compare(QueryMetaData o1, QueryMetaData o2) {
// normal queries before native
if (!Objects.equals(o1.getLanguage(), o2.getLanguage())) {
if (QueryLanguages.LANG_SQL.equals(o1.getLanguage()))
return 1;
else
return -1;
}
return o1.getName().compareTo(o2.getName());
}
@Override
public File getSourceFile() {
return _queries[0].getSourceFile();
}
@Override
public Object getSourceScope() {
return _queries[0].getSourceScope();
}
@Override
public int getSourceType() {
return _queries[0].getSourceType();
}
@Override
public String getResourceName() {
return _queries[0].getResourceName();
}
@Override
public int getLineNumber() {
return _queries[0].getLineNumber();
}
@Override
public int getColNumber() {
return _queries[0].getColNumber();
}
@Override
public int compareTo(ClassQueries other) {
if (other == this)
return 0;
if (other == null)
return -1;
Class scope = (Class) getSourceScope();
Class oscope = (Class) other.getSourceScope();
return scope.getName().compareTo(oscope.getName());
}
}
/**
* Compares clases, sequences, and queries to order them for serialization.
* Places sequences first, then classes, then queries. Sequences and
* queries are ordered alphabetically by name. Classes are placed in
* listing order, in inheritance order within that, and in alphabetical
* order within that.
*
* @author Stephen Kim
*/
protected class SerializationComparator
extends MetaDataInheritanceComparator {
private static final long serialVersionUID = 1L;
@Override
public int compare(Object o1, Object o2) {
if (o1 == o2)
return 0;
if (o1 == null)
return 1;
if (o2 == null)
return -1;
int t1 = type(o1);
int t2 = type(o2);
if (t1 != t2)
return t1 - t2;
switch (t1) {
case TYPE_META:
return compare((ClassMetaData) o1, (ClassMetaData) o2);
case TYPE_QUERY:
return compare((QueryMetaData) o1, (QueryMetaData) o2);
case TYPE_SEQ:
return compare((SequenceMetaData) o1,
(SequenceMetaData) o2);
case TYPE_CLASS_QUERIES:
return ((Comparable) o1).compareTo(o2);
case TYPE_CLASS_SEQS:
return ((Comparable) o1).compareTo(o2);
default:
return compareUnknown(o1, o2);
}
}
/**
* Compare two unrecognized elements of the same type. Throws
* exception by default.
*/
protected int compareUnknown(Object o1, Object o2) {
throw new InternalException();
}
/**
* Compare between two class metadata.
*/
private int compare(ClassMetaData o1, ClassMetaData o2) {
int li1 = o1.getListingIndex();
int li2 = o2.getListingIndex();
if (li1 == -1 && li2 == -1) {
MetaDataTag t1 = getEntityTag(o1);
MetaDataTag t2 = getEntityTag(o2);
if (t1.compareTo(t2) != 0)
return t1.compareTo(t2);
int inher = super.compare(o1, o2);
if (inher != 0)
return inher;
return o1.getDescribedType().getName().compareTo
(o2.getDescribedType().getName());
}
if (li1 == -1)
return 1;
if (li2 == -1)
return -1;
return li1 - li2;
}
/**
* Compare query metadata.
*/
private int compare(QueryMetaData o1, QueryMetaData o2) {
// normal queries before native
if (!Objects.equals(o1.getLanguage(), o2.getLanguage())) {
if (QueryLanguages.LANG_SQL.equals(o1.getLanguage()))
return 1;
else
return -1;
}
return o1.getName().compareTo(o2.getName());
}
/**
* Compare sequence metadata.
*/
private int compare(SequenceMetaData o1, SequenceMetaData o2) {
return o1.getName().compareTo(o2.getName());
}
}
/**
* Sorts fields according to listing order, then XSD strategy order,
* then name order.
*/
private class FieldComparator
implements Comparator {
@Override
public int compare(Object o1, Object o2) {
FieldMetaData fmd1 = (FieldMetaData) o1;
FieldMetaData fmd2 = (FieldMetaData) o2;
if (fmd1.isPrimaryKey()) {
if (fmd2.isPrimaryKey())
return fmd1.compareTo(fmd2);
return -1;
}
if (fmd2.isPrimaryKey())
return 1;
if (fmd1.isVersion()) {
if (fmd2.isVersion())
return compareListingOrder(fmd1, fmd2);
return getStrategy(fmd2) == PersistenceStrategy.BASIC ? 1 : -1;
}
if (fmd2.isVersion())
return getStrategy(fmd1) == PersistenceStrategy.BASIC ? -1 : 1;
int stcmp = getStrategy(fmd1).compareTo(getStrategy(fmd2));
if (stcmp != 0)
return stcmp;
return compareListingOrder(fmd1, fmd2);
}
private int compareListingOrder(FieldMetaData fmd1, FieldMetaData fmd2){
int lcmp = fmd1.getListingIndex() - fmd2.getListingIndex();
if (lcmp != 0)
return lcmp;
return fmd1.compareTo(fmd2);
}
}
/**
* Returns the stored ClassMetaData
*/
public Map<String, ClassMetaData> getClassMetaData() {
return _metas;
}
}