/**********************************************************************
Copyright (c) 2015 Renato Garcia 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:
**********************************************************************/
package org.datanucleus.store.rdbms.mapping.java;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.util.Iterator;
import org.datanucleus.ClassLoaderResolver;
import org.datanucleus.ExecutionContext;
import org.datanucleus.metadata.AbstractMemberMetaData;
import org.datanucleus.metadata.CollectionMetaData;
import org.datanucleus.metadata.ElementMetaData;
import org.datanucleus.metadata.FieldRole;
import org.datanucleus.state.ObjectProvider;
import org.datanucleus.store.rdbms.mapping.MappingCallbacks;
import org.datanucleus.store.rdbms.mapping.datastore.DatastoreMapping;
import org.datanucleus.store.rdbms.table.Table;
import org.datanucleus.store.types.ContainerAdapter;
import org.datanucleus.store.types.ElementContainerHandler;
/**
* Maps single collection elements as 1-1 instead of 1-N, by wrapping and reusing the JavaTypeMappings and member metadata of the element types.
*/
public class SingleCollectionMapping extends JavaTypeMapping implements MappingCallbacks
{
private JavaTypeMapping wrappedMapping;
private Class wrappedTypeClass;
@Override
public void initialize(AbstractMemberMetaData mmd, Table table, ClassLoaderResolver clr)
{
CollectionMetaData collectionMetaData = mmd.getCollection();
String wrappedTypeName = collectionMetaData.getElementType();
wrappedTypeClass = clr.classForName(wrappedTypeName);
WrappedMemberMetaData wmmd = new WrappedMemberMetaData(mmd, wrappedTypeClass, clr);
// Get the actual mapping that handles the the wrapped type
wrappedMapping = table.getStoreManager().getMappingManager().getMapping(table, wmmd, clr, FieldRole.ROLE_FIELD);
super.initialize(mmd, table, clr);
}
@Override
public void setMemberMetaData(AbstractMemberMetaData mmd)
{
super.setMemberMetaData(mmd);
wrappedMapping.setMemberMetaData(new WrappedMemberMetaData(mmd, wrappedTypeClass, getStoreManager().getNucleusContext().getClassLoaderResolver(null)));
}
public JavaTypeMapping getWrappedMapping()
{
return wrappedMapping;
}
@Override
public boolean includeInFetchStatement()
{
return wrappedMapping.includeInFetchStatement();
}
@Override
public boolean hasSimpleDatastoreRepresentation()
{
// TODO: Support non simple
// This is called during query where wrappedMapping is not initialized.
return wrappedMapping == null ? false : wrappedMapping.hasSimpleDatastoreRepresentation();
}
@Override
public void setObject(ExecutionContext ec, PreparedStatement ps, int[] pos, Object container)
{
Object value = null;
if (container != null)
{
ElementContainerHandler containerHandler = ec.getTypeManager().getContainerHandler(mmd.getType());
ContainerAdapter containerAdapter = containerHandler.getAdapter(container);
Iterator iterator = containerAdapter.iterator();
value = iterator.hasNext() ? iterator.next() : null;
}
wrappedMapping.setObject(ec, ps, pos, value);
}
@Override
public Object getObject(ExecutionContext ec, ResultSet rs, int[] exprIndex)
{
Object object = wrappedMapping.getObject(ec, rs, exprIndex);
ElementContainerHandler containerHandler = ec.getTypeManager().getContainerHandler(mmd.getType());
return containerHandler.newContainer(mmd, object);
}
@Override
public Object getObject(ExecutionContext ec, ResultSet rs, int[] exprIndex, ObjectProvider ownerOP, int ownerFieldNumber)
{
throw new RuntimeException("Not implemented yet!");
}
@Override
public int getNumberOfDatastoreMappings()
{
return wrappedMapping.getNumberOfDatastoreMappings();
}
@Override
public DatastoreMapping[] getDatastoreMappings()
{
return wrappedMapping.getDatastoreMappings();
}
@Override
public DatastoreMapping getDatastoreMapping(int index)
{
return wrappedMapping.getDatastoreMapping(index);
}
@Override
public String getJavaTypeForDatastoreMapping(int index)
{
return wrappedMapping.getJavaTypeForDatastoreMapping(index);
}
public void insertPostProcessing(ObjectProvider op)
{
if (wrappedMapping instanceof MappingCallbacks)
{
((MappingCallbacks) wrappedMapping).insertPostProcessing(op);
}
}
public void postInsert(ObjectProvider op)
{
if (wrappedMapping instanceof MappingCallbacks)
{
((MappingCallbacks) wrappedMapping).postInsert(op);
}
}
public void postFetch(ObjectProvider op)
{
if (wrappedMapping instanceof MappingCallbacks)
{
((MappingCallbacks) wrappedMapping).postFetch(op);
}
}
public void postUpdate(ObjectProvider op)
{
if (wrappedMapping instanceof MappingCallbacks)
{
((MappingCallbacks) wrappedMapping).postUpdate(op);
}
}
public void preDelete(ObjectProvider op)
{
if (wrappedMapping instanceof MappingCallbacks)
{
((MappingCallbacks) wrappedMapping).preDelete(op);
}
}
@Override
public Class getJavaType()
{
return wrappedMapping.getJavaType();
}
private class WrappedMemberMetaData extends AbstractMemberMetaData
{
private static final long serialVersionUID = 8346519560709746659L;
private AbstractMemberMetaData singleCollectionMetadata;
public WrappedMemberMetaData(AbstractMemberMetaData fmd, Class type, ClassLoaderResolver clr)
{
super(fmd.getParent(), fmd);
this.singleCollectionMetadata = fmd;
this.type = type;
// Use element definition in preference to field since it may be copied to the element in metadata processing
this.columnMetaData = (fmd.getElementMetaData() != null) ? fmd.getElementMetaData().getColumnMetaData() : fmd.getColumnMetaData();
this.relationType = fmd.getRelationType(clr);
this.relatedMemberMetaData = fmd.getRelatedMemberMetaData(clr);
// Copy the Element embedded definition to the field embedded metaData because EmbeddedPCMapping reads it from there. (Maybe it should use EmbeddedElementPCMapping?)
ElementMetaData fmdElementMetaData = fmd.getElementMetaData();
if (fmdElementMetaData != null && fmdElementMetaData.getEmbeddedMetaData() != null)
{
setEmbeddedMetaData(fmdElementMetaData.getEmbeddedMetaData());
}
}
@Override
public int getAbsoluteFieldNumber()
{
return singleCollectionMetadata.getAbsoluteFieldNumber();
}
@Override
public boolean isDependent()
{
return super.isDependent() || getCollection().isDependentElement();
}
@Override
public String toString()
{
// TODO This is a bastardisation of what the AbstractMemberMetaData contract is supposed to provide, namely the XML form of the metadata! Fix this
return "Wrapped[" + getName() + "]\n" + super.toString();
}
}
}