
org.datanucleus.metadata.EmbeddedMetaData Maven / Gradle / Ivy
Show all versions of datanucleus-core Show documentation
/**********************************************************************
Copyright (c) 2004 Erik Bengtson and others. All rights reserved.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
Contributors:
2004 Andy Jefferson - toString(), MetaData, javadocs
2004 Andy Jefferson - nullIndicatorColumn/Value, ownerField
...
**********************************************************************/
package org.datanucleus.metadata;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Set;
import org.datanucleus.ClassLoaderResolver;
import org.datanucleus.exceptions.ClassNotResolvedException;
import org.datanucleus.util.ClassUtils;
import org.datanucleus.util.Localiser;
import org.datanucleus.util.NucleusLogger;
import org.datanucleus.util.StringUtils;
/**
* This element specifies the mapping for an embedded type. It contains multiple field elements,
* one for each field in the type.
*
* The null-indicator-column optionally identifies the name of the column used to indicate
* whether the embedded instance is null. By default, if the value of this column is null, then the
* embedded instance is null. This column might be mapped to a field of the embedded instance but
* might be a synthetic column for the sole purpose of indicating a null reference.
* The null-indicator-value specifies the value to indicate that the embedded instance is null.
* This is only used for non-nullable columns.
* If null-indicator-column is omitted, then the embedded instance is assumed always to exist.
*/
public class EmbeddedMetaData extends MetaData
{
private static final long serialVersionUID = -1180186183944475444L;
/** Name of the field/property in the embedded object that refers to the owner (bidirectional relation). */
protected String ownerMember;
/** Name of a column used for determining if the embedded object is null */
protected String nullIndicatorColumn;
/** Value in the null column indicating that the embedded object is null */
protected String nullIndicatorValue;
/** Discriminator for use when embedding objects with inheritance. */
protected DiscriminatorMetaData discriminatorMetaData;
/** Fields/properties of the embedded object. */
protected final List members = new ArrayList();
// TODO Drop this and just use "members" above
protected AbstractMemberMetaData memberMetaData[];
/**
* Constructor to create a copy of the passed metadata.
* @param embmd The metadata to copy
*/
public EmbeddedMetaData(EmbeddedMetaData embmd)
{
super(null, embmd);
this.ownerMember = embmd.ownerMember;
this.nullIndicatorColumn = embmd.nullIndicatorColumn;
this.nullIndicatorValue = embmd.nullIndicatorValue;
for (int i=0;i
AbstractClassMetaData embCmd = null; // Definition for the embedded class
String embeddedType = null; // Name of the embedded type
if (md instanceof AbstractMemberMetaData)
{
// PC embedded in PC object
apmd = (AbstractMemberMetaData)md;
embeddedType = apmd.getTypeName();
embCmd = mmgr.getMetaDataForClassInternal(apmd.getType(), clr);
if (embCmd == null && apmd.getFieldTypes() != null && apmd.getFieldTypes().length == 1)
{
// The specified field is not embeddable, nor is it persistent-interface, so try field-type for embedding
embCmd = mmgr.getMetaDataForClassInternal(clr.classForName(apmd.getFieldTypes()[0]), clr);
}
if (embCmd == null)
{
NucleusLogger.METADATA.error(Localiser.msg("044121", apmd.getFullFieldName(), apmd.getTypeName()));
throw new InvalidMemberMetaDataException("044121", apmd.getClassName(), apmd.getName(), apmd.getTypeName());
}
}
else if (md instanceof ElementMetaData)
{
// PC element embedded in collection
ElementMetaData elemmd = (ElementMetaData)md;
apmd = (AbstractMemberMetaData)elemmd.getParent();
embeddedType = apmd.getCollection().getElementType();
try
{
Class cls = clr.classForName(embeddedType, primary);
embCmd = mmgr.getMetaDataForClassInternal(cls, clr);
}
catch (ClassNotResolvedException cnre)
{
// Should be caught by populating the Collection
}
if (embCmd == null)
{
NucleusLogger.METADATA.error(Localiser.msg("044122", apmd.getFullFieldName(), embeddedType));
throw new InvalidMemberMetaDataException("044122", apmd.getClassName(), apmd.getName(), embeddedType);
}
}
else if (md instanceof KeyMetaData)
{
// PC key embedded in Map
KeyMetaData keymd = (KeyMetaData)md;
apmd = (AbstractMemberMetaData)keymd.getParent();
embeddedType = apmd.getMap().getKeyType();
try
{
Class cls = clr.classForName(embeddedType, primary);
embCmd = mmgr.getMetaDataForClassInternal(cls, clr);
}
catch (ClassNotResolvedException cnre)
{
// Should be caught by populating the Map
}
if (embCmd == null)
{
NucleusLogger.METADATA.error(Localiser.msg("044123", apmd.getFullFieldName(), embeddedType));
throw new InvalidMemberMetaDataException("044123", apmd.getClassName(), apmd.getName(), embeddedType);
}
}
else if (md instanceof ValueMetaData)
{
// PC value embedded in Map
ValueMetaData valuemd = (ValueMetaData)md;
apmd = (AbstractMemberMetaData)valuemd.getParent();
embeddedType = apmd.getMap().getValueType();
try
{
Class cls = clr.classForName(embeddedType, primary);
embCmd = mmgr.getMetaDataForClassInternal(cls, clr);
}
catch (ClassNotResolvedException cnre)
{
// Should be caught by populating the Map
}
if (embCmd == null)
{
NucleusLogger.METADATA.error(Localiser.msg("044124", apmd.getFullFieldName(), embeddedType));
throw new InvalidMemberMetaDataException("044124", apmd.getClassName(), apmd.getName(), embeddedType);
}
}
// Check that all "members" are of the correct type for the embedded object
Iterator memberIter = members.iterator();
while (memberIter.hasNext())
{
Object fld = memberIter.next();
// TODO Should allow PropertyMetaData here I think
if (embCmd instanceof InterfaceMetaData && fld instanceof FieldMetaData)
{
// Cannot have a field within a persistent interface
throw new InvalidMemberMetaDataException("044129", apmd.getClassName(), apmd.getName(), ((AbstractMemberMetaData)fld).getName());
}
}
Set memberNames = new HashSet<>();
for (AbstractMemberMetaData mmd : members)
{
memberNames.add(mmd.getName());
}
// Add fields for the class that aren't in the block using Reflection.
// NOTE 1 : We ignore fields in superclasses
// NOTE 2 : We ignore "enhanced" fields (added by the JDO enhancer)
// NOTE 3 : We ignore inner class fields (containing "$")
// NOTE 4 : We sort the fields into ascending alphabetical order
Class embeddedClass = null;
Collections.sort(members);
try
{
// Load the embedded class
embeddedClass = clr.classForName(embeddedType, primary);
// TODO Cater for properties in the populating class when the user defines using setters
// Get all (reflected) fields in the populating class
Field[] cls_fields=embeddedClass.getDeclaredFields();
for (int i=0;i iter = members.iterator();
while (iter.hasNext())
{
AbstractMemberMetaData md = iter.next();
if (mmd.getName().equals(md.getName()))
{
throw new InvalidMemberMetaDataException("044112", mmd.getClassName(), mmd.getName());
}
}
members.add(mmd);
mmd.parent = this;
}
/**
* Method to create a new FieldMetaData, add it, and return it.
* @param name Name of the field
* @return The FieldMetaData
*/
public FieldMetaData newFieldMetaData(String name)
{
FieldMetaData fmd = new FieldMetaData(this, name);
addMember(fmd);
return fmd;
}
/**
* Method to create a new PropertyMetaData, add it, and return it.
* @param name Name of the property
* @return The PropertyMetaData
*/
public PropertyMetaData newPropertyMetaData(String name)
{
PropertyMetaData pmd = new PropertyMetaData(this, name);
addMember(pmd);
return pmd;
}
// ------------------------------- Utilities -------------------------------
/**
* Returns a string representation of the object using a prefix
* This can be used as part of a facility to output a MetaData file.
* @param prefix prefix string
* @param indent indent string
* @return a string representation of the object.
*/
public String toString(String prefix,String indent)
{
// Field needs outputting so generate metadata
StringBuilder sb = new StringBuilder();
sb.append(prefix).append("\n");
if (discriminatorMetaData != null)
{
sb.append(discriminatorMetaData.toString(prefix+indent, indent));
}
// Add fields
for (int i=0; i\n");
return sb.toString();
}
}