blob: 170cf2426bd2d0726a4e80bdef7df92dec06095e [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.apache.openjpa.persistence;
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.io.Writer;
import java.lang.annotation.Annotation;
import java.lang.reflect.Field;
import java.lang.reflect.Member;
import java.lang.reflect.Method;
import java.security.AccessController;
import java.security.PrivilegedActionException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.Comparator;
import java.util.EnumSet;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Objects;
import java.util.Properties;
import java.util.Set;
import javax.persistence.AttributeOverride;
import javax.persistence.Basic;
import javax.persistence.CascadeType;
import javax.persistence.Embeddable;
import javax.persistence.Embedded;
import javax.persistence.EmbeddedId;
import javax.persistence.Entity;
import javax.persistence.FetchType;
import javax.persistence.Id;
import javax.persistence.IdClass;
import javax.persistence.ManyToMany;
import javax.persistence.ManyToOne;
import javax.persistence.MapKey;
import javax.persistence.MappedSuperclass;
import javax.persistence.NamedNativeQuery;
import javax.persistence.NamedQuery;
import javax.persistence.OneToMany;
import javax.persistence.OneToOne;
import javax.persistence.OrderBy;
import javax.persistence.QueryHint;
import javax.persistence.SequenceGenerator;
import javax.persistence.Transient;
import javax.persistence.Version;
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.SourceTracker;
import org.apache.openjpa.lib.util.ClassUtil;
import org.apache.openjpa.lib.util.J2DoPrivHelper;
import org.apache.openjpa.lib.util.JavaVersions;
import org.apache.openjpa.lib.util.Localizer;
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.MetaDataModes;
import org.apache.openjpa.meta.MetaDataRepository;
import org.apache.openjpa.meta.Order;
import org.apache.openjpa.meta.QueryMetaData;
import org.apache.openjpa.meta.SequenceMetaData;
import org.apache.openjpa.meta.ValueMetaData;
import org.apache.openjpa.util.InternalException;
//@todo: javadocs
/**
* Serializes persistence metadata as annotations.
* 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 1.0.0
* @author Steve Kim
* @author Gokhan Ergul
*/
public class AnnotationPersistenceMetaDataSerializer
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
(AnnotationPersistenceMetaDataSerializer.class);
private Log _log = null;
private final OpenJPAConfiguration _conf;
private Map<String, ClassMetaData> _metas = null;
private Map<String, List> _queries = null;
private Map<String, List> _seqs = null;
private int _mode = MetaDataModes.MODE_NONE;
private SerializationComparator _comp = null;
private Map<ClassMetaData, List<AnnotationBuilder>> _clsAnnos = null;
private Map<FieldMetaData, List<AnnotationBuilder>> _fldAnnos = null;
private Map<SequenceMetaData, List<AnnotationBuilder>> _seqAnnos = null;
private Map<QueryMetaData, List<AnnotationBuilder>> _qryAnnos = null;
/**
* Constructor. Supply configuration.
*/
public AnnotationPersistenceMetaDataSerializer(OpenJPAConfiguration conf) {
_conf = conf;
setLog(conf.getLog(OpenJPAConfiguration.LOG_METADATA));
setMode(MetaDataModes.MODE_META | MetaDataModes.MODE_MAPPING |
MetaDataModes.MODE_QUERY);
}
/**
* Configuration.
*/
public OpenJPAConfiguration getConfiguration() {
return _conf;
}
/**
* The log to write to.
*/
public Log getLog() {
return _log;
}
/**
* The log to write to.
*/
public void setLog(Log log) {
_log = log;
}
/**
* 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 == MetaDataModes.MODE_NONE)
setMode(MetaDataModes.MODE_NONE);
else if (on)
setMode(_mode | mode);
else
setMode(_mode & ~mode);
}
/**
* Convenience method for interpreting {@link #getMode}.
*/
protected boolean isMetaDataMode() {
return (_mode & MetaDataModes.MODE_META) != 0;
}
/**
* Convenience method for interpreting {@link #getMode}.
*/
protected boolean isQueryMode() {
return (_mode & MetaDataModes.MODE_QUERY) != 0;
}
/**
* Convenience method for interpreting {@link #getMode}.
*/
protected boolean isMappingMode() {
return (_mode & MetaDataModes.MODE_MAPPING) != 0;
}
/**
* Convenience method for interpreting {@link #getMode}. Takes into
* account whether mapping information is loaded for the given instance.
* OPENJPA-1360 - Allow @Column attributes when meta.isEmbeddedOnly()
*/
protected boolean isMappingMode(ClassMetaData meta) {
return isMappingMode() && (meta.getSourceMode()
& MetaDataModes.MODE_MAPPING) != 0
&& (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 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 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 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 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();
}
/**
* 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 == MetaDataModes.MODE_QUERY || _metas == null
|| !_metas.containsKey(entry.getKey()))
all.add(new ClassQueries((List<QueryMetaData>)
entry.getValue()));
}
}
/**
* Creates a new annotation builder for the specified annotation type.
*/
protected AnnotationBuilder newAnnotationBuilder(
Class<? extends Annotation> annType) {
return new AnnotationBuilder(annType);
}
protected void addAnnotation(AnnotationBuilder ab, Object meta) {
if (meta instanceof ClassMetaData)
addAnnotation(ab, (ClassMetaData) meta);
else if (meta instanceof FieldMetaData)
addAnnotation(ab, (FieldMetaData) meta);
else if (meta instanceof SequenceMetaData)
addAnnotation(ab, (SequenceMetaData) meta);
else if (meta instanceof QueryMetaData)
addAnnotation(ab, (QueryMetaData) meta);
}
/**
* Add an annotation builder to list of builders for the specified
* class metadata.
*/
protected void addAnnotation(AnnotationBuilder ab, ClassMetaData meta) {
if (_clsAnnos == null)
_clsAnnos = new HashMap<>();
List<AnnotationBuilder> list = _clsAnnos.computeIfAbsent(meta, k -> new ArrayList<>());
list.add(ab);
}
/**
* Add an annotation builder to list of builders for the specified
* field metadata.
*/
protected void addAnnotation(AnnotationBuilder ab, FieldMetaData meta) {
if (_fldAnnos == null)
_fldAnnos = new HashMap<>();
List<AnnotationBuilder> list = _fldAnnos.computeIfAbsent(meta, k -> new ArrayList<>());
list.add(ab);
}
/**
* Add an annotation builder to list of builders for the specified
* sequence metadata.
*/
protected void addAnnotation(AnnotationBuilder ab, SequenceMetaData meta) {
if (_seqAnnos == null)
_seqAnnos = new HashMap<>();
List<AnnotationBuilder> list = _seqAnnos.computeIfAbsent(meta, k -> new ArrayList<>());
list.add(ab);
}
/**
* Add an annotation builder to list of builders for the specified
* query metadata.
*/
protected void addAnnotation(AnnotationBuilder ab, QueryMetaData meta) {
if (_qryAnnos == null)
_qryAnnos = new HashMap<>();
List<AnnotationBuilder> list = _qryAnnos.computeIfAbsent(meta, k -> new ArrayList<>());
list.add(ab);
}
/**
* Creates an an annotation builder for the specified class metadata
* and adds it to list of builders.
*/
protected AnnotationBuilder addAnnotation(
Class<? extends Annotation> annType, ClassMetaData meta) {
AnnotationBuilder ab = newAnnotationBuilder(annType);
if (meta == null)
return ab;
addAnnotation(ab, meta);
return ab;
}
/**
* Creates an an annotation builder for the specified class metadata
* and adds it to list of builders.
*/
protected AnnotationBuilder addAnnotation(
Class<? extends Annotation> annType, FieldMetaData meta) {
AnnotationBuilder ab = newAnnotationBuilder(annType);
if (meta == null)
return ab;
addAnnotation(ab, meta);
return ab;
}
/**
* Creates an an annotation builder for the specified class metadata
* and adds it to list of builders.
*/
protected AnnotationBuilder addAnnotation(
Class<? extends Annotation> annType, SequenceMetaData meta) {
AnnotationBuilder ab = newAnnotationBuilder(annType);
if (meta == null)
return ab;
addAnnotation(ab, meta);
return ab;
}
/**
* Creates an an annotation builder for the specified class metadata
* and adds it to list of builders.
*/
protected AnnotationBuilder addAnnotation(
Class<? extends Annotation> annType, QueryMetaData meta) {
AnnotationBuilder ab = newAnnotationBuilder(annType);
if (meta == null)
return ab;
addAnnotation(ab, meta);
return ab;
}
protected void serialize(Collection objects) {
for (Object obj : objects) {
int type = type(obj);
switch (type) {
case TYPE_META:
serializeClass((ClassMetaData) obj);
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;
}
}
}
/**
* 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 unknown mapping element at system level.
*/
protected void serializeSystemMappingElement(Object obj) {
}
/**
* Serialize query metadata.
*/
private void serializeQuery(QueryMetaData meta) {
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()));
}
Class<? extends Annotation> ann =
QueryLanguages.LANG_SQL.equals(meta.getLanguage()) ?
NamedNativeQuery.class : NamedQuery.class;
AnnotationBuilder abQry = addAnnotation(ann, meta);
abQry.add("name", meta.getName());
abQry.add("query", meta.getQueryString());
if (QueryLanguages.LANG_SQL.equals(meta.getLanguage())) {
if (meta.getResultType() != null)
abQry.add("resultClass", meta.getResultType());
}
serializeQueryHints(meta, abQry);
}
/**
* Serialize query hints.
*/
private void serializeQueryHints(QueryMetaData meta, AnnotationBuilder ab) {
String[] hints = meta.getHintKeys();
Object[] values = meta.getHintValues();
for (int i = 0; i < hints.length; i++) {
AnnotationBuilder abHint = newAnnotationBuilder(QueryHint.class);
abHint.add("name", hints[i]);
abHint.add("value", String.valueOf(values[i]));
ab.add("hints", abHint);
}
}
/**
* Serialize sequence metadata.
*/
protected void serializeSequence(SequenceMetaData meta) {
Log log = getLog();
if (log.isInfoEnabled())
log.info(_loc.get("ser-sequence", meta.getName()));
AnnotationBuilder ab = addAnnotation(SequenceGenerator.class, meta);
ab.add("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)
ab.add("sequenceName", ds);
else if (plugin != null && !SequenceMetaData.IMPL_NATIVE.equals
(plugin))
ab.add("sequenceName", plugin);
if (meta.getInitialValue() != 0 && meta.getInitialValue() != -1)
ab.add("initialValue", meta.getInitialValue());
if (meta.getAllocate() != 50 && meta.getAllocate() != -1)
ab.add("allocationSize", meta.getAllocate());
if (meta.getSchema() != null)
ab.add("schema", meta.getSchema());
if (meta.getCatalog() != null)
ab.add("catalog", meta.getCatalog());
}
/**
* Serialize class metadata.
*/
protected void serializeClass(ClassMetaData meta) {
Log log = getLog();
if (log.isInfoEnabled())
log.info(_loc.get("ser-class", meta));
AnnotationBuilder abEntity = addAnnotation(
getEntityAnnotationType(meta), meta);
if (isMetaDataMode()
&& !meta.getTypeAlias().equals(ClassUtil.getClassName(meta.
getDescribedType())))
abEntity.add("name", meta.getTypeAlias());
if (isMappingMode())
addClassMappingAnnotations(meta);
if (isMappingMode())
serializeClassMappingContent(meta);
if (isMetaDataMode())
serializeIdClass(meta);
if (isMappingMode())
serializeInheritanceContent(meta);
if (isMappingMode()) {
List 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())) {
FieldMetaData orig;
for (FieldMetaData fmd : fields) {
if (fmd.getDeclaringType() != fmd.getDefiningMetaData().
getDescribedType()) {
orig = fmd.getDeclaringMetaData().getDeclaredField
(fmd.getName());
} else
orig = null;
serializeField(fmd, orig);
}
}
}
/**
* Return entity annotation type.
*/
private static Class<? extends Annotation> getEntityAnnotationType(
ClassMetaData meta) {
switch (getEntityTag(meta)) {
case ENTITY:
return Entity.class;
case EMBEDDABLE:
return Embeddable.class;
case MAPPED_SUPERCLASS:
return MappedSuperclass.class;
default:
throw new IllegalStateException();
}
}
/**
* Return field annotation type.
*/
private static Class<? extends Annotation> getFieldAnnotationType (
FieldMetaData fmd, PersistenceStrategy strat) {
Class<? extends Annotation> ann = null;
if (fmd.isPrimaryKey() && strat == PersistenceStrategy.EMBEDDED)
ann = EmbeddedId.class;
else if (fmd.isPrimaryKey())
ann = Id.class;
else if (fmd.isVersion())
ann = Version.class;
else {
switch (strat) {
case TRANSIENT:
ann = Transient.class;
break;
case BASIC:
ann = Basic.class;
break;
case EMBEDDED:
ann = Embedded.class;
break;
case MANY_ONE:
ann = ManyToOne.class;
break;
case ONE_ONE:
ann = OneToOne.class;
break;
case ONE_MANY:
ann = OneToMany.class;
break;
case MANY_MANY:
ann = ManyToMany.class;
break;
}
}
return ann;
}
/**
* Return the MetaDataTag for the given class meta data.
*/
private static MetaDataTag getEntityTag(ClassMetaData meta) {
if (meta.isAbstract())
return MetaDataTag.MAPPED_SUPERCLASS;
// @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;
}
/**
* Add mapping attributes for the given class. Does nothing by default
*/
protected void addClassMappingAnnotations(ClassMetaData mapping) {
}
/**
* Serialize id-class.
*/
private void serializeIdClass(ClassMetaData meta) {
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())) {
AnnotationBuilder ab = addAnnotation(IdClass.class, meta);
ab.add(null, oid);
}
}
/**
* Serialize class mapping content. Does nothing by default.
*/
protected void serializeClassMappingContent(ClassMetaData mapping) {
}
/**
* Serialize inheritance content. Does nothing by default.
*/
protected void serializeInheritanceContent(ClassMetaData mapping) {
}
/**
* Serialize query mappings. Does nothing by default.
*/
protected void serializeQueryMappings(ClassMetaData meta) {
}
/**
* Serialize the given field.
*/
private void serializeField(FieldMetaData fmd, FieldMetaData orig) {
if (fmd.getManagement() != FieldMetaData.MANAGE_PERSISTENT
&& !fmd.isExplicit())
return;
PersistenceStrategy strat = getStrategy(fmd);
ValueMetaData cascades = null;
AnnotationBuilder ab = addAnnotation(
getFieldAnnotationType (fmd, strat), fmd);
if (fmd.isPrimaryKey() && strat == PersistenceStrategy.EMBEDDED)
; // noop
else if (fmd.isPrimaryKey())
; // noop
else if (fmd.isVersion())
; // noop
else {
switch (strat) {
case BASIC:
if (isMetaDataMode())
addBasicAttributes(fmd, ab);
break;
case MANY_ONE:
if (isMetaDataMode())
addManyToOneAttributes(fmd, ab);
cascades = fmd;
break;
case ONE_ONE:
if (isMetaDataMode())
addOneToOneAttributes(fmd, ab);
cascades = fmd;
break;
case ONE_MANY:
if (isMetaDataMode())
addOneToManyAttributes(fmd, ab);
cascades = fmd.getElement();
break;
case MANY_MANY:
if (isMetaDataMode())
addManyToManyAttributes(fmd, ab);
cascades = fmd.getElement();
break;
}
if (isMappingMode())
addStrategyMappingAttributes(fmd, ab);
}
if (isMappingMode(fmd))
addFieldMappingAttributes(fmd, orig, ab);
if (fmd.getOrderDeclaration() != null) {
if (!(Order.ELEMENT + " asc").equals(fmd.getOrderDeclaration()))
addAnnotation(OrderBy.class, fmd).
add (null, fmd.getOrderDeclaration());
}
if (isMappingMode() && fmd.getKey().getValueMappedBy() != null) {
AnnotationBuilder abMapKey = addAnnotation(MapKey.class, fmd);
FieldMetaData mapBy = fmd.getKey().getValueMappedByMetaData();
if (!mapBy.isPrimaryKey() ||
mapBy.getDefiningMetaData().getPrimaryKeyFields().length != 1) {
abMapKey.add("name", fmd.getKey().getValueMappedBy());
}
}
if (isMappingMode(fmd))
serializeFieldMappingContent(fmd, strat, ab);
if (cascades != null && isMetaDataMode())
serializeCascades(cascades, ab);
}
/**
* Add mapping attributes for the given field. Does nothing by default.
*/
protected void addFieldMappingAttributes(FieldMetaData fmd,
FieldMetaData orig, AnnotationBuilder ab) {
}
/**
* 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) {
AnnotationBuilder ab = addAnnotation(AttributeOverride.class, fmd);
ab.add("name", fmd.getName());
serializeAttributeOverrideMappingContent(fmd, orig, ab);
}
/**
* Serialize attribute override mapping content. Does nothing by default,
*/
protected void serializeAttributeOverrideMappingContent
(FieldMetaData fmd, FieldMetaData orig, AnnotationBuilder ab) {
}
/**
* Serialize cascades.
*/
private void serializeCascades(ValueMetaData vmd, AnnotationBuilder ab) {
EnumSet<CascadeType> cascades = EnumSet.noneOf(CascadeType.class);
if (vmd.getCascadePersist() == ValueMetaData.CASCADE_IMMEDIATE) {
cascades.add(CascadeType.PERSIST);
}
if (vmd.getCascadeAttach() == ValueMetaData.CASCADE_IMMEDIATE) {
cascades.add(CascadeType.MERGE);
}
if (vmd.getCascadeDelete() == ValueMetaData.CASCADE_IMMEDIATE) {
cascades.add(CascadeType.REMOVE);
}
if (vmd.getCascadeRefresh() == ValueMetaData.CASCADE_IMMEDIATE) {
cascades.add(CascadeType.REFRESH);
}
if (vmd.getCascadeDetach() == ValueMetaData.CASCADE_IMMEDIATE) {
cascades.add(CascadeType.DETACH);
}
if (cascades.size() == 5) // ALL
{
cascades.clear();
cascades.add(CascadeType.ALL);
}
if (!cascades.isEmpty()) {
ab.add("cascade", cascades);
}
}
/**
* 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:
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, AnnotationBuilder ab) {
if (!fmd.isInDefaultFetchGroup())
ab.add("fetch", FetchType.LAZY);
if (fmd.getNullValue() == FieldMetaData.NULL_EXCEPTION)
ab.add("optional", false);
}
/**
* Add many-to-one attributes.
*/
private void addManyToOneAttributes(FieldMetaData fmd,
AnnotationBuilder ab) {
if (!fmd.isInDefaultFetchGroup())
ab.add("fetch", FetchType.LAZY);
if (fmd.getNullValue() == FieldMetaData.NULL_EXCEPTION)
ab.add("optional", false);
}
/**
* Add one-to-one attributes.
*/
private void addOneToOneAttributes(FieldMetaData fmd,
AnnotationBuilder ab) {
if (!fmd.isInDefaultFetchGroup())
ab.add("fetch", FetchType.LAZY);
if (fmd.getNullValue() == FieldMetaData.NULL_EXCEPTION)
ab.add("optional", false);
}
/**
* Add one-to-many attributes.
*/
private void addOneToManyAttributes(FieldMetaData fmd,
AnnotationBuilder ab) {
if (fmd.isInDefaultFetchGroup())
ab.add("fetch", FetchType.EAGER);
addTargetEntityAttribute(fmd, ab);
}
/**
* Add many-to-many attributes.
*/
private void addManyToManyAttributes(FieldMetaData fmd,
AnnotationBuilder ab) {
if (fmd.isInDefaultFetchGroup())
ab.add("fetch", FetchType.EAGER);
addTargetEntityAttribute(fmd, ab);
}
/**
* Add a target-entity attribute to collection and map fields that do
* not use generics.
*/
private void addTargetEntityAttribute(FieldMetaData fmd,
AnnotationBuilder ab) {
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)
ab.add("targetEntity", fmd.getElement().getDeclaredType());
break;
case JavaTypes.MAP:
if (types.length != 2)
ab.add("targetEntity", fmd.getElement().getDeclaredType());
break;
}
}
/**
* Serialize field mapping content; this will be called before
* {@link #serializeValueMappingContent}. Does nothing by default.
*/
protected void serializeFieldMappingContent(FieldMetaData fmd, PersistenceStrategy strategy, AnnotationBuilder ab) {
}
/**
* Set mapping attributes for strategy. Sets mapped-by by default.
*/
protected void addStrategyMappingAttributes(FieldMetaData fmd,
AnnotationBuilder ab) {
if (fmd.getMappedBy() != null)
ab.add("mappedBy", fmd.getMappedBy());
}
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;
}
protected void writeAnnotations(Object meta,
List<AnnotationBuilder> builders, Map output) {
List<String> annos = new ArrayList<>();
for(AnnotationBuilder ab: builders)
annos.add(ab.toString());
output.put(meta, annos);
}
@Override
public void serialize(Map output, int flags) throws IOException {
Collection all = getObjects();
serialize(all);
if (_clsAnnos != null)
for (ClassMetaData meta : _clsAnnos.keySet())
writeAnnotations(meta, _clsAnnos.get(meta), output);
if (_fldAnnos != null)
for (FieldMetaData meta : _fldAnnos.keySet())
writeAnnotations(meta, _fldAnnos.get(meta), output);
if (_seqAnnos != null)
for (SequenceMetaData meta : _seqAnnos.keySet())
writeAnnotations(meta, _seqAnnos.get(meta), output);
if (_qryAnnos != null)
for (QueryMetaData meta : _qryAnnos.keySet())
writeAnnotations(meta, _qryAnnos.get(meta), output);
}
@Override
public void serialize(File file, int flags) throws IOException {
try {
FileWriter out = new FileWriter(AccessController
.doPrivileged(J2DoPrivHelper.getCanonicalPathAction(file)),
(flags & APPEND) > 0);
serialize(out, flags);
out.close();
} catch (PrivilegedActionException pae) {
throw (IOException) pae.getException();
}
}
@Override
@SuppressWarnings("unchecked")
public void serialize(Writer out, int flags) throws IOException {
Map output = new HashMap();
serialize(output, flags);
Set<Entry> entrySet = output.entrySet();
for(Entry entry : entrySet) {
Object meta = entry.getKey();
List<String> annos = (List<String>) entry.getValue();
out.write("--"+meta.toString());
out.write("\n");
for(String ann: annos) {
out.write("\t");
out.write(ann);
out.write("\n");
}
}
}
@Override
public void serialize(int flags) throws IOException {
throw new UnsupportedOperationException();
}
/**
* Represents ordered set of
* {@link org.apache.openjpa.meta.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 org.apache.openjpa.meta.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;
}
}