blob: 4783f1ab0f87ebbe51c4cf6297c2129795572d7b [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2011, 2013 Oracle and/or its affiliates. All rights reserved.
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License v1.0 and Eclipse Distribution License v. 1.0
* which accompanies this distribution.
* The Eclipse Public License is available at http://www.eclipse.org/legal/epl-v10.html
* and the Eclipse Distribution License is available at
* http://www.eclipse.org/org/documents/edl-v10.php.
*
* Contributors:
* dclarke/tware - initial
******************************************************************************/
package org.eclipse.persistence.jpa.rs.util;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.StringTokenizer;
import org.eclipse.persistence.descriptors.ClassDescriptor;
import org.eclipse.persistence.dynamic.DynamicEntity;
import org.eclipse.persistence.internal.descriptors.PersistenceEntity;
import org.eclipse.persistence.internal.dynamic.DynamicEntityImpl;
import org.eclipse.persistence.internal.helper.DatabaseField;
import org.eclipse.persistence.internal.jpa.CMP3Policy;
import org.eclipse.persistence.internal.sessions.AbstractSession;
import org.eclipse.persistence.jpa.JpaHelper;
import org.eclipse.persistence.jpa.rs.PersistenceContext;
import org.eclipse.persistence.mappings.DatabaseMapping;
import org.eclipse.persistence.mappings.OneToOneMapping;
import org.eclipse.persistence.queries.FetchGroupTracker;
import org.eclipse.persistence.sessions.DatabaseSession;
/**
* EclipseLink helper class used for converting composite key values passed into
* JAX-RS calls as query or matrix parameters into a value that can be used in a
* find.
*
* @author dclarke
* @since EclipseLink 2.4.0
*/
public class IdHelper {
private static final String SEPARATOR_STRING = "+";
@SuppressWarnings("rawtypes")
public static Object buildId(PersistenceContext app, String entityName, String idString) {
DatabaseSession session = app.getJpaSession();
ClassDescriptor descriptor = app.getDescriptor(entityName);
List<DatabaseMapping> pkMappings = descriptor.getObjectBuilder().getPrimaryKeyMappings();
List<SortableKey> pkIndices = new ArrayList<SortableKey>();
int index = 0;
int multitenantPKMappings = 0;
for (DatabaseMapping mapping: pkMappings){
if (mapping.isMultitenantPrimaryKeyMapping()){
multitenantPKMappings++;
} else {
pkIndices.add(new SortableKey(mapping, index));
index++;
}
}
Collections.sort(pkIndices);
// Handle composite key in map
Object[] keyElements = new Object[pkMappings.size() - multitenantPKMappings];
StringTokenizer tokenizer = new StringTokenizer(idString, SEPARATOR_STRING);
int tokens = tokenizer.countTokens();
if (tokens + multitenantPKMappings != pkMappings.size()){
throw new RuntimeException("Failed, incorrect number of keys values");
}
index = 0;
Iterator<SortableKey> iterator = pkIndices.iterator();
while (tokenizer.hasMoreTokens()){
SortableKey key = iterator.next();
String token = tokenizer.nextToken();
DatabaseMapping mapping = key.getMapping();
Class attributeClasification = mapping.getAttributeClassification();
if (attributeClasification == null) {
if ((mapping.getFields() != null) && (!mapping.getFields().isEmpty())) {
attributeClasification = mapping.getFields().get(0).getType();
}
}
Object idValue = session.getDatasourcePlatform().getConversionManager().convertObject(token, attributeClasification);
keyElements[key.getIndex()] = idValue;
index++;
}
if (descriptor.hasCMPPolicy()) {
CMP3Policy policy = (CMP3Policy) descriptor.getCMPPolicy();
return policy.createPrimaryKeyInstanceFromPrimaryKeyValues((AbstractSession) session, new int[]{0}, keyElements);
}
if (keyElements.length == 1) {
return keyElements[0];
}
return keyElements;
}
public static String stringifyId(Object entity, String typeName, PersistenceContext app) {
ClassDescriptor descriptor = app.getDescriptor(typeName);
List<DatabaseMapping> pkMappings = descriptor.getObjectBuilder().getPrimaryKeyMappings();
if (pkMappings.isEmpty()) {
return "";
}
List<SortableKey> pkIndices = new ArrayList<SortableKey>();
int index = 0;
for (DatabaseMapping mapping : pkMappings) {
pkIndices.add(new SortableKey(mapping, index));
index++;
}
Collections.sort(pkIndices);
StringBuffer key = new StringBuffer();
Iterator<SortableKey> sortableKeys = pkIndices.iterator();
List<DatabaseField> refObjectdbFields = null;
while (sortableKeys.hasNext()) {
DatabaseMapping mapping = sortableKeys.next().getMapping();
ClassDescriptor refDesc = mapping.getReferenceDescriptor();
List<DatabaseField> dbFields = mapping.getDescriptor().getPrimaryKeyFields();
if (refDesc != null) {
refObjectdbFields = refDesc.getFields();
}
if ((refObjectdbFields != null) && (!refObjectdbFields.isEmpty())) {
for (DatabaseField dbField : dbFields) {
String dbFieldName = dbField.getName();
String refObjectDbFieldName = null;
if (refDesc != null) {
for (DatabaseField refObjectDbField : refObjectdbFields) {
refObjectDbFieldName = refObjectDbField.getName();
if ((refObjectDbFieldName != null) && (dbFieldName != null)) {
if (dbFieldName.equals(refObjectDbFieldName)) {
List<DatabaseMapping> refMappings = refDesc.getMappings();
for (DatabaseMapping refMapping : refMappings) {//
DatabaseField field = refMapping.getField();
if (field != null) {
String fieldName = field.getName();
if (mapping instanceof OneToOneMapping) {
Map<DatabaseField, DatabaseField> targetToSourceKeyFields = ((OneToOneMapping) mapping).getTargetToSourceKeyFields();
Map<DatabaseField, DatabaseField> sourceToTargetFields = ((OneToOneMapping) mapping).getTargetToSourceKeyFields();
if ((targetToSourceKeyFields != null) && (!targetToSourceKeyFields.isEmpty())) {
if (targetToSourceKeyFields.containsKey(refObjectDbField)) {
if ((sourceToTargetFields != null) && (!sourceToTargetFields.isEmpty())) {
if (sourceToTargetFields.containsKey(field)) {
if ((fieldName != null) && (dbFieldName.equals(fieldName))) {
Object value = descriptor.getObjectBuilder().getBaseValueForField(dbField, entity);
Object realAttributeValue = refMapping.getRealAttributeValueFromAttribute(refMapping.getAttributeValueFromObject(value), value, (AbstractSession) app.getJpaSession());
key.append(realAttributeValue);
}
}
}
}
}
}
}
}
}
}
}
}
}
} else {
Object part = mapping.getAttributeValueFromObject(entity);
key.append(part);
}
if (sortableKeys.hasNext()) {
key.append(SEPARATOR_STRING);
refObjectdbFields = null;
}
}
return key.toString();
}
/**
* build a shell of an object based on a primary key. The object shell will be an instance of the object with
* only primary key populated
* @param context
* @param entityType
* @param id
* @return
*/
public static Object buildObjectShell(PersistenceContext context, String entityType, Object id) {
ClassDescriptor descriptor = context.getDescriptor(entityType);
List<DatabaseMapping> pkMappings = descriptor.getObjectBuilder().getPrimaryKeyMappings();
Object entity = null;
if (descriptor.hasCMPPolicy()) {
CMP3Policy policy = (CMP3Policy) descriptor.getCMPPolicy();
entity = policy.createBeanUsingKey(id, (AbstractSession) context.getJpaSession());
} else if (entity instanceof DynamicEntity) {
DynamicEntityImpl dynamicEntity = (DynamicEntityImpl) context.newEntity(entityType);
// if there is only one PK mapping, we assume the id object
// represents the value of that mapping
if (pkMappings.size() == 1) {
dynamicEntity.set(pkMappings.get(0).getAttributeName(), id);
} else {
// If there are more that one PK, we assume an array as produced
// by buildId() above with the keys
// based on a sorted order of PK fields
List<SortableKey> pkIndices = new ArrayList<SortableKey>();
int index = 0;
for (DatabaseMapping mapping : pkMappings) {
pkIndices.add(new SortableKey(mapping, index));
index++;
}
Collections.sort(pkIndices);
Object[] keyElements = (Object[]) id;
for (SortableKey key : pkIndices) {
dynamicEntity.set(key.getMapping().getAttributeName(), keyElements[key.getIndex()]);
}
}
entity = dynamicEntity;
} else {
throw new RuntimeException("Could not create shell for entity.");
}
if (entity instanceof PersistenceEntity) {
((PersistenceEntity) entity)._persistence_setId(id);
}
if (entity instanceof FetchGroupTracker) {
((FetchGroupTracker) entity)._persistence_setSession(JpaHelper.getDatabaseSession(context.getEmf()));
}
return entity;
}
private static class SortableKey implements Comparable<SortableKey>{
private DatabaseMapping mapping;
private int index;
public SortableKey(DatabaseMapping mapping, int index){
this.mapping = mapping;
this.index = index;
}
public int compareTo(SortableKey o){
return mapping.getAttributeName().compareTo(o.getMapping().getAttributeName());
}
public DatabaseMapping getMapping(){
return mapping;
}
public int getIndex(){
return index;
}
}
}