blob: dd097602b0729af061b92c2a531a6d0b1b429e6f [file] [log] [blame]
/*
* Copyright (c) 2011, 2021 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 v. 2.0 which is available at
* http://www.eclipse.org/legal/epl-2.0,
* or the Eclipse Distribution License v. 1.0 which is available at
* http://www.eclipse.org/org/documents/edl-v10.php.
*
* SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
*/
// 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.getServerSession();
ClassDescriptor descriptor = app.getDescriptor(entityName);
List<DatabaseMapping> pkMappings = descriptor.getObjectBuilder().getPrimaryKeyMappings();
List<SortableKey> pkIndices = new ArrayList<>();
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<>();
int index = 0;
for (DatabaseMapping mapping : pkMappings) {
pkIndices.add(new SortableKey(mapping, index));
index++;
}
Collections.sort(pkIndices);
StringBuilder key = new StringBuilder();
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.getServerSession());
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
*/
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.getServerSession());
} else if (DynamicEntity.class.isAssignableFrom(descriptor.getJavaClass())) {
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;
}
public static List<SortableKey> getPrimaryKey(PersistenceContext context, String entityName) {
ClassDescriptor descriptor = context.getDescriptor(entityName);
List<DatabaseMapping> pkMappings = descriptor.getObjectBuilder().getPrimaryKeyMappings();
List<SortableKey> pkIndices = new ArrayList<SortableKey>();
int index = 0;
for (DatabaseMapping mapping : pkMappings) {
if (!mapping.isMultitenantPrimaryKeyMapping()) {
pkIndices.add(new SortableKey(mapping, index));
index++;
}
}
Collections.sort(pkIndices);
return pkIndices;
}
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;
}
@Override
public int compareTo(SortableKey o) {
if (this.equals(o)) {
return 0;
} else if (mapping.getAttributeName().equals(o.getMapping().getAttributeName())) {
return Integer.compare(index, o.getIndex());
} else {
return mapping.getAttributeName().compareTo(o.getMapping().getAttributeName());
}
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
SortableKey that = (SortableKey) o;
if (index != that.index) return false;
if (!mapping.equals(that.mapping)) return false;
return true;
}
@Override
public int hashCode() {
int result = mapping.hashCode();
result = 31 * result + index;
return result;
}
public DatabaseMapping getMapping() {
return mapping;
}
public int getIndex() {
return index;
}
}
}