blob: d2218504c7e66dd666703ff070aa8851d7388c6b [file] [log] [blame]
/********************************************************************************
* Copyright (c) 2015-2020 Contributors to the Eclipse Foundation
*
* See the NOTICE file(s) distributed with this work for additional
* information regarding copyright ownership.
*
* 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.
*
* SPDX-License-Identifier: EPL-2.0
*
********************************************************************************/
package org.eclipse.mdm.businessobjects.utils;
import java.lang.reflect.Method;
import java.util.List;
import java.util.function.Function;
import java.util.stream.Collectors;
import javax.ws.rs.core.GenericEntity;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;
import javax.ws.rs.core.Response.Status;
import org.eclipse.mdm.api.base.ServiceNotProvidedException;
import org.eclipse.mdm.api.base.adapter.Core;
import org.eclipse.mdm.api.base.adapter.EntityType;
import org.eclipse.mdm.api.base.adapter.ModelManager;
import org.eclipse.mdm.api.base.model.BaseEntity;
import org.eclipse.mdm.api.base.model.ContextRoot;
import org.eclipse.mdm.api.base.model.ContextType;
import org.eclipse.mdm.api.base.model.Entity;
import org.eclipse.mdm.api.base.model.Environment;
import org.eclipse.mdm.api.base.model.ValueType;
import org.eclipse.mdm.api.base.query.ComparisonOperator;
import org.eclipse.mdm.api.base.query.Condition;
import org.eclipse.mdm.api.base.query.Filter;
import org.eclipse.mdm.api.base.query.FilterItem;
import org.eclipse.mdm.api.dflt.ApplicationContext;
import org.eclipse.mdm.api.dflt.EntityManager;
import org.eclipse.mdm.api.dflt.model.*;
import org.eclipse.mdm.businessobjects.control.FilterParser;
import org.eclipse.mdm.businessobjects.entity.I18NResponse;
import org.eclipse.mdm.businessobjects.entity.MDMEntityResponse;
import org.eclipse.mdm.businessobjects.entity.SearchAttributeResponse;
import org.eclipse.mdm.businessobjects.service.EntityService;
import org.slf4j.LoggerFactory;
import io.vavr.Value;
import io.vavr.collection.Map;
import io.vavr.collection.Stream;
import io.vavr.control.Try;
public final class ServiceUtils {
private ServiceUtils() {
}
/**
* returns true if the given filter String is a parent filter of the given
* parent type
*
* @param em {@link EntityManager} of the data source
* @param filter parent filter string to check
* @param parentType class of the parent entity
* @return true if the give filter String is a parent filter
*/
public static boolean isParentFilter(ApplicationContext context, String filter,
Class<? extends Entity> parentType) {
ModelManager mm = context.getModelManager()
.orElseThrow(() -> new ServiceNotProvidedException(ModelManager.class));
EntityType et = mm.getEntityType(parentType);
Filter f = FilterParser.parseFilterString(mm.listEntityTypes(), filter);
List<FilterItem> filterItems = f.stream().collect(Collectors.toList());
if (filterItems.size() == 1 && filterItems.get(0).isCondition()) {
Condition c = filterItems.get(0).getCondition();
return et.getIDAttribute().equals(c.getAttribute())
&& ComparisonOperator.EQUAL.equals(c.getComparisonOperator());
} else {
return false;
}
}
/**
* returns the business object ID from a parent filter
*
* @param em {@link EntityManager} of the data source
* @param filter parent filter string
* @param parentType parent type to identify the Id attribute name
* @return the extracted business object Id
* @throws IllegalArgumentException if the given filter is not a parent filter,
* this means the filter does not have exactly
* one condition on the parent's ID attribute
* with {@link ComparisonOperator#EQUAL}
*/
public static String extactIdFromParentFilter(ApplicationContext context, String filter,
Class<? extends Entity> parentType) {
ModelManager mm = context.getModelManager()
.orElseThrow(() -> new ServiceNotProvidedException(ModelManager.class));
EntityType et = mm.getEntityType(parentType);
Filter f = FilterParser.parseFilterString(mm.listEntityTypes(), filter);
List<FilterItem> filterItems = f.stream().collect(Collectors.toList());
if (filterItems.size() == 1 && filterItems.get(0).isCondition()) {
Condition c = filterItems.get(0).getCondition();
if (et.getIDAttribute().equals(c.getAttribute())
&& ComparisonOperator.EQUAL.equals(c.getComparisonOperator())) {
return c.getValue().extract(ValueType.STRING);
}
}
throw new IllegalArgumentException("Cannot extract parent ID. Filter is not a parent filter: " + filter);
}
/**
* Simple workaround for naming mismatch between Adapter and Business object
* names.
*
* @param entityType entity type
* @return MDM business object name
*/
public static String workaroundForTypeMapping(EntityType entityType) {
switch (entityType.getName()) {
case "StructureLevel":
return "Pool";
case "MeaResult":
return "Measurement";
case "SubMatrix":
return "ChannelGroup";
case "MeaQuantity":
return "Channel";
default:
return entityType.getName();
}
}
/**
* Builds {@Link Response} from given {@link Entity}
*
* @param entity {@link Entity} to build {@link Response} from
* @return the build {@link Response}
*/
public static <T extends Entity> Response buildEntityResponse(T entity, Status status) {
if (entity != null) {
MDMEntityResponse response = new MDMEntityResponse(entity.getClass(), entity);
// TODO anehmer on 2018-02-08: relations should be included in the output
GenericEntity<Object> genEntity = new GenericEntity<>(response, response.getClass());
return Response.status(status).entity(genEntity).type(MediaType.APPLICATION_JSON).build();
} else {
return Response.status(Status.NO_CONTENT).type(MediaType.APPLICATION_JSON).build();
}
}
/**
* Builds {@Link Response} from given {@link Entity}
*
* @param entity {@link Entity} to build {@link Response} from
* @return the build {@link Response} or empty response if entities is empty
*/
public static <T extends Entity> Response buildEntityResponse(io.vavr.collection.List<T> entities, Status status) {
if (entities.nonEmpty()) {
@SuppressWarnings("unchecked")
Class<T> entityClass = (Class<T>) entities.get().getClass();
MDMEntityResponse response = new MDMEntityResponse(entityClass, entities.asJava());
GenericEntity<Object> genEntity = new GenericEntity<>(response, response.getClass());
return Response.status(status).entity(genEntity).type(MediaType.APPLICATION_JSON).build();
} else {
return Response.status(Status.NO_CONTENT).type(MediaType.APPLICATION_JSON).build();
}
}
/**
* Builds {@Link Response} from given {@link Entity}
*
* @param entity {@link Entity} to build {@link Response} from
* @param clazz default class of entity. Used for empty collections.
* @return the build {@link Response}
*/
@SuppressWarnings("unchecked")
public static <T extends Entity> Response buildEntityResponse(io.vavr.collection.List<T> entities, Status status,
Class<T> clazz) {
if (entities != null) {
Class<T> entityClass;
if (entities.nonEmpty()) {
entityClass = (Class<T>) entities.get().getClass();
} else {
entityClass = clazz;
}
MDMEntityResponse response = new MDMEntityResponse(entityClass, entities.asJava());
GenericEntity<Object> genEntity = new GenericEntity<>(response, response.getClass());
return Response.status(status).entity(genEntity).type(MediaType.APPLICATION_JSON).build();
} else {
return Response.status(Status.NO_CONTENT).type(MediaType.APPLICATION_JSON).build();
}
}
/**
* Builds {@Link Response} from given {@link Entity}
*
* @param entity {@link Entity} to build {@link Response} from
* @return the build {@link Response}
*/
public static <T extends Entity> Response buildErrorResponse(Throwable t, Status status) {
return Response.status(status).entity(t).type(MediaType.APPLICATION_JSON).build();
}
/**
* Converts the given object to a {@link Response} with the given {@link Status}
*
* @param response object to convert
* @param status {@link Status} of the {@link Response}
* @return the created {@link Response}
*/
public static Response toResponse(Object response, Status status) {
GenericEntity<Object> genEntity = new GenericEntity<>(response, response.getClass());
return Response.status(status).entity(genEntity).type(MediaType.APPLICATION_JSON).build();
}
/**
* Return the search attributes for the {@link ValueList} type.
*
* @param sourceNameSupplier {@link Value} with the name of the source (MDM
* {@link org.eclipse.mdm.api.base.model.Environment}
* name)
* @param entityClass {@link Entity} class to get localization data for
* @param entityService {@link EntityService} used to get localization data
* @return the result of the delegated request as {@link Response}
*/
public static <T extends Entity> Response buildSearchAttributesResponse(Value<String> sourceNameSupplier,
Class<T> entityClass, EntityService entityService) {
return entityService.getSearchAttributesSupplier(sourceNameSupplier, entityClass)
.map(searchAttributes -> ServiceUtils
.toResponse(new SearchAttributeResponse(searchAttributes.toJavaList()), Status.OK))
.recover(ServiceUtils.ERROR_RESPONSE_SUPPLIER).getOrElse(ServiceUtils.SERVER_ERROR_RESPONSE);
}
/**
* Return the localized type and attributes for the {@link Entity} type.
*
* @param sourceNameSupplier {@link Value} with the name of the source (MDM
* {@link Environment} name)
* @param entityClass {@link Entity} class to get localization data for
* @param entityService {@link EntityService} used to get localization data
* @return the {@link Response} with the localized data
*/
public static <T extends Entity> Response buildLocalizationResponse(Value<String> sourceNameSupplier,
Class<T> entityClass, EntityService entityService) {
return Try.of(() -> ServiceUtils.toResponse(
new I18NResponse(
entityService.getLocalizeTypeSupplier(sourceNameSupplier, entityClass).get().toJavaMap(),
entityService.getLocalizeAttributesSupplier(sourceNameSupplier, entityClass).get().toJavaMap()),
Status.OK)).recover(ServiceUtils.ERROR_RESPONSE_SUPPLIER).getOrElse(ServiceUtils.SERVER_ERROR_RESPONSE);
}
/**
* A Response representing a server error.
*/
public static final Response SERVER_ERROR_RESPONSE = Response.serverError().build();
/**
* Builds an error response based on an exception to be sent to the client
*/
public static final Function<? super Throwable, Response> ERROR_RESPONSE_SUPPLIER = e -> {
LoggerFactory.getLogger(ServiceUtils.class).error(e.getMessage(), e);
// TODO anehmer on 2017-11-22: customize status according to exception
return Response
.status(Status.INTERNAL_SERVER_ERROR).entity(e.getStackTrace()[0].getClassName() + "."
+ e.getStackTrace()[0].getMethodName() + ": " + e.getMessage())
.type(MediaType.APPLICATION_JSON).build();
};
/**
* Returns a {@link Try} to get the {@link ContextType} for the provided name
*
* @param contextTypeName name of the {@link ContextType}
* @return a {@link Try} of the {@link ContextType} for the given name
*/
public static Try<ContextType> getContextTypeSupplier(String contextTypeName) {
return Stream.of(ContextType.values())
.filter(contextType -> contextType.name().equals(contextTypeName.toUpperCase())).toTry();
}
/**
* Converts vavr style context map to java.util style context map
*/
public static java.util.Map<String, java.util.Map<ContextType, ContextRoot>> contextMapToJava(
Map<String, Map<ContextType, ContextRoot>> ctx) {
return ctx.bimap(s -> s, m -> m.toJavaMap()).toJavaMap();
}
/**
* Get {@link ContextType} from given entity if it has one. Otherwise return
* null.
*
* @param entity {@link Entity} to get {@link ContextType} for
* @return {@link ContextType} or null
*/
public static ContextType getContextType(Entity entity) {
if (entity instanceof CatalogAttribute) {
return ((CatalogAttribute) entity).getCatalogComponent().map(CatalogComponent::getContextType)
.orElseGet(null);
} else if (entity instanceof CatalogComponent) {
return ((CatalogComponent) entity).getContextType();
} else if (entity instanceof TemplateRoot) {
return ((TemplateRoot) entity).getContextType();
} else if (entity instanceof TemplateComponent) {
return ((TemplateComponent) entity).getTemplateRoot().getContextType();
} else if (entity instanceof TemplateAttribute) {
return ((TemplateAttribute) entity).getTemplateComponent().map(TemplateComponent::getTemplateRoot)
.map(TemplateRoot::getContextType).orElseGet(null);
}
return null;
}
/**
* Get the {@link org.eclipse.mdm.api.base.adapter.Core} of given
* {@link org.eclipse.mdm.api.base.model.Entity}
*
* @param e Entity to get Core of
* @return Core of given Entity
*/
public static Core getCore(Entity e) {
return Try.of(() -> {
Method getMetod;
try {
getMetod = BaseEntity.class.getDeclaredMethod("getCore");
getMetod.setAccessible(true);
} catch (NoSuchMethodException | SecurityException x) {
throw new IllegalStateException(
"Unable to load 'getCore()' in class '" + BaseEntity.class.getSimpleName() + "'.", x);
}
return (Core) getMetod.invoke(e);
}).get();
}
}