package io.cattle.platform.object.meta.impl;
import static io.cattle.platform.object.meta.Relationship.RelationshipType.*;
import io.cattle.platform.archaius.util.ArchaiusUtil;
import io.cattle.platform.engine.process.ProcessDefinition;
import io.cattle.platform.engine.process.StateTransition;
import io.cattle.platform.object.jooq.utils.JooqUtils;
import io.cattle.platform.object.meta.ActionDefinition;
import io.cattle.platform.object.meta.MapRelationship;
import io.cattle.platform.object.meta.ObjectMetaDataManager;
import io.cattle.platform.object.meta.Relationship;
import io.cattle.platform.object.meta.TypeSet;
import io.cattle.platform.object.util.DataAccessor;
import io.cattle.platform.object.util.DataUtils;
import io.cattle.platform.util.type.CollectionUtils;
import io.cattle.platform.util.type.InitializationTask;
import io.cattle.platform.util.type.Priority;
import io.github.ibuildthecloud.gdapi.condition.ConditionType;
import io.github.ibuildthecloud.gdapi.factory.SchemaFactory;
import io.github.ibuildthecloud.gdapi.factory.impl.SchemaFactoryImpl;
import io.github.ibuildthecloud.gdapi.factory.impl.SchemaPostProcessor;
import io.github.ibuildthecloud.gdapi.model.Action;
import io.github.ibuildthecloud.gdapi.model.Field;
import io.github.ibuildthecloud.gdapi.model.FieldType;
import io.github.ibuildthecloud.gdapi.model.Filter;
import io.github.ibuildthecloud.gdapi.model.Schema;
import io.github.ibuildthecloud.gdapi.model.impl.FieldImpl;
import io.github.ibuildthecloud.gdapi.model.impl.SchemaImpl;
import java.beans.PropertyDescriptor;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.TreeMap;
import java.util.TreeSet;
import java.util.WeakHashMap;
import javax.annotation.PostConstruct;
import javax.inject.Inject;
import javax.inject.Named;
import javax.persistence.Column;
import org.apache.commons.beanutils.PropertyUtils;
import org.apache.commons.lang3.tuple.ImmutablePair;
import org.apache.commons.lang3.tuple.Pair;
import org.jooq.ForeignKey;
import org.jooq.Table;
import org.jooq.TableField;
import com.google.common.collect.Lists;
public class DefaultObjectMetaDataManager implements ObjectMetaDataManager, InitializationTask, Priority {
SchemaFactory schemaFactory;
List<TypeSet> typeSets;
Map<Class<?>, Map<String, List<Relationship>>> relationships = new HashMap<Class<?>, Map<String, List<Relationship>>>();
Map<Class<?>, Map<String, List<Relationship>>> relationshipsBothCase = new HashMap<Class<?>, Map<String, List<Relationship>>>();
List<ProcessDefinition> processDefinitions;
Map<String, Set<String>> validStates = new HashMap<String, Set<String>>();
Map<String, Set<String>> transitioningStates = new HashMap<String, Set<String>>();
Map<String, Set<String>> actions = new HashMap<String, Set<String>>();
Map<String, Map<String, String>> linksCache = Collections.synchronizedMap(new WeakHashMap<String, Map<String, String>>());
Map<FieldCacheKey, String> propertyCache = Collections.synchronizedMap(new WeakHashMap<FieldCacheKey, String>());
Map<String, Map<String, ActionDefinition>> actionDefinitions = new HashMap<String, Map<String, ActionDefinition>>();
Map<FieldCacheKey, TableField<?, ?>> tableFields = new HashMap<FieldCacheKey, TableField<?, ?>>();
@PostConstruct
public void init() {
if (schemaFactory instanceof SchemaFactoryImpl) {
((SchemaFactoryImpl) schemaFactory).getPostProcessors().add(0, new SchemaPostProcessor() {
@Override
public SchemaImpl postProcessRegister(SchemaImpl schema, SchemaFactory factory) {
return DefaultObjectMetaDataManager.this.postProcessRegister(schema, factory);
}
@Override
public SchemaImpl postProcess(SchemaImpl schema, SchemaFactory factory) {
return DefaultObjectMetaDataManager.this.postProcess(schema, factory);
}
});
}
}
@Override
public void start() {
List<Schema> schemas = registerTypes();
registerActions();
registerActionDefinitions();
registerStates();
registerRelationships();
parseSchemas(schemas);
}
protected void registerActions() {
actions.clear();
for (ProcessDefinition def : processDefinitions) {
String resourceType = def.getResourceType();
Set<String> actions = this.actions.get(resourceType);
if (resourceType == null) {
continue;
}
if (def.getName().startsWith(resourceType.toLowerCase() + ".")) {
if (actions == null) {
actions = new LinkedHashSet<String>();
this.actions.put(resourceType, actions);
}
actions.add(def.getName().substring(resourceType.length() + 1));
}
}
}
protected void registerStates() {
transitioningStates.clear();
for (ProcessDefinition def : processDefinitions) {
Set<String> validStates = this.validStates.get(def.getResourceType());
Set<String> states = transitioningStates.get(def.getResourceType());
for (StateTransition transition : def.getStateTransitions()) {
if (states == null) {
states = new HashSet<String>();
transitioningStates.put(def.getResourceType(), states);
}
if (validStates == null) {
validStates = new TreeSet<String>();
this.validStates.put(def.getResourceType(), validStates);
}
switch (transition.getType()) {
case DONE:
states.add(transition.getFromState());
break;
case TRANSITIONING:
states.add(transition.getToState());
break;
default:
}
if (ObjectMetaDataManager.STATE_FIELD.equals(transition.getField())) {
validStates.add(transition.getToState());
validStates.add(transition.getFromState());
}
}
}
}
protected void registerActionDefinitions() {
actionDefinitions.clear();
for (ProcessDefinition processDef : processDefinitions) {
String type = processDef.getResourceType();
String processName = processDef.getName();
if (processName.startsWith(type.toLowerCase())) {
processName = processName.substring(type.length() + 1, processName.length());
}
Map<String, ActionDefinition> actionDefs = actionDefinitions.get(type);
if (actionDefs == null) {
actionDefs = new HashMap<String, ActionDefinition>();
actionDefinitions.put(type, actionDefs);
}
ActionDefinition def = actionDefs.get(processName);
if (def == null) {
def = new ActionDefinition();
actionDefs.put(processName, def);
}
for (StateTransition transition : processDef.getStateTransitions()) {
if (transition.getType() != StateTransition.Style.DONE) {
def.getValidStates().add(transition.getFromState());
}
}
}
}
protected void registerRelationships() {
for (TypeSet typeSet : typeSets) {
for (Class<?> clz : typeSet.getTypeClasses()) {
Table<?> table = JooqUtils.getTableFromRecordClass(clz);
if (table == null) {
continue;
}
registerTableFields(table);
for (ForeignKey<?, ?> reference : table.getReferences()) {
register(reference);
}
}
}
findMappings();
}
protected void findMappings() {
Map<Class<?>, List<Pair<Class<?>, Relationship>>> foundRelationship = new HashMap<Class<?>, List<Pair<Class<?>, Relationship>>>();
for (Map.Entry<Class<?>, Map<String, List<Relationship>>> entry : relationships.entrySet()) {
for (Map.Entry<String, List<Relationship>> relEntry : entry.getValue().entrySet()) {
List<Relationship> relList = relEntry.getValue();
for (Relationship rel : relList) {
if (rel.getRelationshipType() == Relationship.RelationshipType.CHILD) {
Schema schema = schemaFactory.getSchema(rel.getObjectType());
if (schema != null && schema.getId().endsWith(ObjectMetaDataManager.MAP_SUFFIX)) {
CollectionUtils.addToMap(foundRelationship, rel.getObjectType(),
(Pair<Class<?>, Relationship>) new ImmutablePair<Class<?>, Relationship>(entry.getKey(), rel), ArrayList.class);
}
}
}
}
}
for (Map.Entry<Class<?>, List<Pair<Class<?>, Relationship>>> entry : foundRelationship.entrySet()) {
List<Pair<Class<?>, Relationship>> rels = entry.getValue();
if (rels.size() > 2) {
Iterator<Pair<Class<?>, Relationship>> it = rels.iterator();
while (it.hasNext()) {
Pair<Class<?>, Relationship> rel = it.next();
boolean ignoreRelationship = excludeRelationShip(entry.getKey().getSimpleName(), rel.getRight()
.getPropertyName());
if (ignoreRelationship) {
it.remove();
}
}
}
if (rels.size() != 2) {
continue;
}
Pair<Class<?>, Relationship> left = rels.get(0);
Pair<Class<?>, Relationship> right = rels.get(1);
String mapRightToLeftName = right.getRight().getPropertyName();
String mapLeftToRightName = left.getRight().getPropertyName();
if (left.getLeft().equals(right.getLeft())) {
// This basically looks like a junction map to itself.
// Inspect the column names for extra info and use the link
// override to obtain a more meaningful link name
register(getLinkNameOverride(entry.getKey().getSimpleName(), mapRightToLeftName, mapRightToLeftName), entry.getKey(), left, right);
register(getLinkNameOverride(entry.getKey().getSimpleName(), mapLeftToRightName, mapLeftToRightName), entry.getKey(), right, left);
} else {
register(getLinkNameOverride(
entry.getKey().getSimpleName(),
mapRightToLeftName,
schemaFactory.getSchema(right.getLeft()).getPluralName()
),
entry.getKey(),
left, right);
register(getLinkNameOverride(
entry.getKey().getSimpleName(),
mapLeftToRightName,
schemaFactory.getSchema(left.getLeft()).getPluralName()
),
entry.getKey(),
right, left);
}
}
}
private String getLinkNameOverride(String objectName, String property, String defaultName) {
String mapNameOverride = ArchaiusUtil.getString(String.format("object.link.name.%s.%s.override", objectName, property).toLowerCase()).get();
return mapNameOverride == null ? defaultName : mapNameOverride;
}
private boolean excludeRelationShip(String objectName, String fieldToIgnore) {
return ArchaiusUtil.getBoolean(
String.format("object.link.ignore.%s.%s", objectName, fieldToIgnore).toLowerCase()).get();
}
protected void register(String mappingName, Class<?> mappingType, Pair<Class<?>, Relationship> left, Pair<Class<?>, Relationship> right) {
register(left.getLeft(), new MapRelationshipImpl(mappingName, mappingType, right.getLeft(), left.getRight(), right.getRight()));
}
protected void registerTableFields(Table<?> table) {
Class<?> clz = table.getClass();
for (java.lang.reflect.Field field : clz.getFields()) {
if (TableField.class.isAssignableFrom(field.getType()) && Modifier.isPublic(field.getModifiers())) {
try {
field.setAccessible(true);
TableField<?, ?> tableField = (TableField<?, ?>) field.get(table);
String name = getNameFromField(table.getRecordType(), tableField.getName());
tableFields.put(new FieldCacheKey(table.getRecordType(), name), tableField);
} catch (IllegalArgumentException e) {
throw new IllegalStateException(e);
} catch (IllegalAccessException e) {
throw new IllegalStateException(e);
}
}
}
}
protected List<Schema> registerTypes() {
List<Schema> schemas = new ArrayList<Schema>();
for (TypeSet typeSet : typeSets) {
for (Class<?> type : typeSet.getTypeClasses()) {
Schema schema = schemaFactory.registerSchema(type);
if (schema != null)
schemas.add(schema);
}
for (String name : typeSet.getTypeNames()) {
Schema schema = schemaFactory.registerSchema(name);
if (schema != null)
schemas.add(schema);
}
}
return schemas;
}
protected void parseSchemas(List<Schema> schemas) {
Set<String> done = new HashSet<String>();
List<Schema> todo = new ArrayList<Schema>(schemas);
do {
int todoSize = todo.size();
Iterator<Schema> iter = todo.iterator();
while (iter.hasNext()) {
Schema schema = iter.next();
if (schema.getParent() == null || done.contains(schema.getParent())) {
schemaFactory.parseSchema(schema.getId());
done.add(schema.getId());
iter.remove();
}
}
if (todo.size() > 0 && todo.size() == todoSize) {
throw new IllegalStateException("Failed to find parents of schemas " + todo);
}
} while (todo.size() > 0);
}
protected void register(ForeignKey<?, ?> foreignKey) {
TableField<?, ?>[] fields = foreignKey.getFieldsArray();
if (fields.length == 0 || fields.length > 1) {
return;
}
TableField<?, ?> field = fields[0];
if (!field.getDataType().isNumeric()) {
return;
}
String propertyName = getNameFromField(field.getTable().getRecordType(), field.getName());
String referenceName = propertyName;
if (field.getName().endsWith(ID_FIELD)) {
referenceName = referenceName.substring(0, referenceName.length() - 2);
}
Class<?> localType = foreignKey.getTable().getRecordType();
Class<?> foreignType = foreignKey.getKey().getTable().getRecordType();
Schema localSchema = schemaFactory.getSchema(localType);
String childName = localSchema.getPluralName();
String childNameOverride = ArchaiusUtil.getString(
String.format("object.link.name.%s.%s.override", localType.getSimpleName(), propertyName).toLowerCase()).get();
if (childNameOverride != null) {
childName = childNameOverride;
}
register(localType, new ForeignKeyRelationship(REFERENCE, referenceName, propertyName, foreignType, foreignKey));
register(foreignType, new ForeignKeyRelationship(CHILD, childName, propertyName, localType, foreignKey));
}
protected void register(Class<?> type, Relationship relationship) {
register(this.relationships, type, relationship, false);
register(this.relationshipsBothCase, type, relationship, true);
}
protected void register(Map<Class<?>, Map<String, List<Relationship>>> relationshipsMap, Class<?> type, Relationship relationship, boolean bothCase) {
Map<String, List<Relationship>> relationships = relationshipsMap.get(type);
if (relationships == null) {
relationships = new HashMap<String, List<Relationship>>();
relationshipsMap.put(type, relationships);
}
String name = relationship.getName().toLowerCase();
List<Relationship> existing = relationships.get(name);
if (existing != null) {
existing.add(relationship);
if (bothCase) {
relationships.get(relationship.getName()).add(relationship);
}
return;
}
relationships.put(relationship.getName().toLowerCase(), Lists.newArrayList(relationship));
if (bothCase) {
relationships.put(relationship.getName(), Lists.newArrayList(relationship));
}
}
@Override
public Object convertFieldNameFor(String type, Object key) {
return getTableFieldFor(type, key);
}
protected TableField<?, ?> getTableFieldFor(String type, Object key) {
if (key instanceof TableField<?, ?>)
return (TableField<?, ?>) key;
Class<?> clz = schemaFactory.getSchemaClass(type);
FieldCacheKey cacheKey = new FieldCacheKey(clz, key.toString());
return tableFields.get(cacheKey);
}
@Override
public String convertToPropertyNameString(Class<?> recordClass, Object key) {
if (key instanceof String) {
return (String) key;
}
if (key instanceof TableField) {
TableField<?, ?> field = (TableField<?, ?>) key;
return getNameFromField(recordClass, field.getName());
}
return key == null ? null : key.toString();
}
@Override
public String lookupPropertyNameFromFieldName(Class<?> recordClass, String fieldName) {
return getNameFromField(recordClass, fieldName);
}
protected String getNameFromField(Class<?> clz, String field) {
FieldCacheKey key = new FieldCacheKey(clz, field);
String cached = propertyCache.get(key);
if (cached != null)
return cached;
for (PropertyDescriptor desc : PropertyUtils.getPropertyDescriptors(clz)) {
Method readMethod = desc.getReadMethod();
Method writeMethod = desc.getWriteMethod();
if (readMethod == null || writeMethod == null) {
continue;
}
Column column = readMethod.getAnnotation(Column.class);
if (column != null && field.equals(column.name())) {
propertyCache.put(key, desc.getName());
return desc.getName();
}
}
throw new IllegalArgumentException("Failed to find bean property for table field [" + field + "] on [" + clz + "]");
}
public SchemaImpl postProcessRegister(SchemaImpl schema, SchemaFactory factory) {
return schema;
}
protected void addStates(SchemaImpl schema, SchemaFactory factory) {
String type = factory.getBaseType(schema.getId());
Set<String> validStates = this.validStates.get(type);
Field stateField = schema.getResourceFields().get(ObjectMetaDataManager.STATE_FIELD);
if (validStates != null && stateField instanceof FieldImpl) {
FieldImpl field = (FieldImpl) stateField;
field.setOptions(new ArrayList<String>(validStates));
field.setTypeEnum(FieldType.ENUM);
}
}
protected void addActions(SchemaImpl schema, SchemaFactory factory) {
Set<String> actions = this.actions.get(schema.getId());
if (actions == null || actions.size() == 0) {
return;
}
Map<String, Action> resourceActions = schema.getResourceActions();
if (resourceActions == null) {
resourceActions = new LinkedHashMap<String, Action>();
schema.setResourceActions(resourceActions);
}
for (String action : actions) {
if (!resourceActions.containsKey(action)) {
Action newAction = new Action();
newAction.setOutput(schema.getId());
resourceActions.put(action, newAction);
}
}
}
protected void addTransitioningFields(SchemaImpl schema, SchemaFactory factory) {
Set<String> states = transitioningStates.get(schema.getId());
if (states == null || states.size() == 0) {
return;
}
addField(schema, TRANSITIONING_FIELD, FieldType.ENUM, TRANSITIONING_YES, TRANSITIONING_NO, TRANSITIONING_ERROR);
addField(schema, TRANSITIONING_MESSAGE_FIELD, FieldType.STRING);
addField(schema, TRANSITIONING_PROGRESS_FIELD, FieldType.INT);
}
protected void addField(SchemaImpl schema, String name, FieldType type, String... options) {
Field f = schema.getResourceFields().get(name);
if (f != null) {
return;
}
FieldImpl newField = new FieldImpl();
newField.setTypeEnum(type);
newField.setName(name);
if (type == FieldType.ENUM) {
newField.setOptions(Arrays.asList(options));
} else {
newField.setNullable(true);
}
schema.getResourceFields().put(name, newField);
}
public SchemaImpl postProcess(SchemaImpl schema, SchemaFactory factory) {
addStates(schema, factory);
addActions(schema, factory);
addTransitioningFields(schema, factory);
Map<String, List<Relationship>> relationships = this.relationships.get(factory.getSchemaClass(schema.getId()));
if (relationships != null) {
List<Relationship> allRelationships = new ArrayList<Relationship>();
for (List<Relationship> relationshipList : relationships.values()) {
allRelationships.addAll(relationshipList);
}
for (Relationship relationship : allRelationships) {
String linkName = relationship.getName();
if (relationship.getRelationshipType() != REFERENCE) {
schema.getIncludeableLinks().add(linkName);
continue;
}
Field field = schema.getResourceFields().get(relationship.getPropertyName());
if (!(field instanceof FieldImpl)) {
continue;
}
FieldImpl fieldImpl = (FieldImpl) field;
fieldImpl.setType(FieldType.toString(FieldType.REFERENCE, factory.getSchema(relationship.getObjectType()).getId()));
schema.getIncludeableLinks().add(linkName);
}
}
Map<String, Filter> filters = schema.getCollectionFilters();
for (Map.Entry<String, Field> entry : schema.getResourceFields().entrySet()) {
String name = entry.getKey();
Field field = entry.getValue();
if (!(field instanceof FieldImpl)) {
continue;
}
FieldImpl fieldImpl = (FieldImpl) field;
TableField<?, ?> tableField = getTableFieldFor(schema.getId(), name);
if (tableField != null && !filters.containsKey(name)) {
List<String> modifiers = getModifiers(fieldImpl);
if (modifiers.size() > 0) {
filters.put(name, new Filter(modifiers));
}
}
}
return schema;
}
protected List<String> getModifiers(FieldImpl field) {
FieldType type = field.getTypeEnum();
if (type == null) {
return Collections.emptyList();
}
List<String> conditions = new ArrayList<String>(type.getModifiers().size() + 2);
for (ConditionType conditionType : type.getModifiers()) {
conditions.add(conditionType.getExternalForm());
}
if (field.isNullable()) {
if (!conditions.contains(ConditionType.NULL.getExternalForm())) {
conditions.add(ConditionType.NULL.getExternalForm());
}
if (!conditions.contains(ConditionType.NOTNULL.getExternalForm())) {
conditions.add(ConditionType.NOTNULL.getExternalForm());
}
}
return conditions;
}
@Override
public Map<String, Relationship> getLinkRelationships(SchemaFactory schemaFactory, String type) {
if (schemaFactory == null) {
schemaFactory = this.schemaFactory;
}
Map<String, Relationship> result = new HashMap<String, Relationship>();
Schema schema = schemaFactory.getSchema(type);
if (null == schema) {
return null;
}
Map<String, List<Relationship>> relationships = this.relationships.get(schemaFactory.getSchemaClass(schema.getId()));
if (relationships == null) {
return result;
}
for (String link : getLinks(schemaFactory, type).keySet()) {
link = link.toLowerCase();
List<Relationship> relList = relationships.get(link);
if (relList != null) {
if (relList.size() > 1) {
for (Relationship rel : relList) {
if (rel.getName().toLowerCase().equals(link)) {
result.put(link, rel);
}
}
} else {
result.put(link, relList.get(0));
}
}
}
return result;
}
@Override
public Map<String, String> getLinks(SchemaFactory schemaFactory, String type) {
if (schemaFactory == null) {
schemaFactory = this.schemaFactory;
}
String key = schemaFactory.getId() + ":links:" + type;
Map<String, String> links = linksCache.get(key);
if (links != null)
return links;
links = new TreeMap<String, String>();
Schema schema = schemaFactory.getSchema(type);
Map<String, List<Relationship>> relationships = this.relationships.get(schemaFactory.getSchemaClass(schema.getId(), true));
if (relationships == null || relationships.size() == 0) {
linksCache.put(key, links);
return links;
}
List<Relationship> allRelationships = new ArrayList<Relationship>();
for (List<Relationship> relationshipList : relationships.values()) {
allRelationships.addAll(relationshipList);
}
for (Relationship relationship : allRelationships) {
if (relationship.isListResult()) {
Schema other = schemaFactory.getSchema(relationship.getObjectType());
if (other != null)
links.put(relationship.getName(), null);
} else {
if (schema.getResourceFields().containsKey(relationship.getPropertyName())) {
links.put(relationship.getName(), relationship.getPropertyName());
}
}
}
linksCache.put(key, links);
return links;
}
@Override
public Relationship getRelationship(String type, String linkName) {
Class<?> clz = schemaFactory.getSchemaClass(type, true);
if (clz == null) {
return null;
}
Map<String, List<Relationship>> relationship = relationshipsBothCase.get(clz);
List<Relationship> relationshipList = relationship.get(linkName);
return getRelationship(relationshipList, linkName);
}
@Override
public Relationship getRelationship(Class<?> clz, String linkName) {
if (clz == null) {
return null;
}
Map<String, List<Relationship>> relationship = relationshipsBothCase.get(clz);
List<Relationship> relationshipList = relationship.get(linkName);
return getRelationship(relationshipList, linkName);
}
private Relationship getRelationship(List<Relationship> relationshipList, String linkName) {
if (relationshipList == null || relationshipList.size() == 0) {
return null;
}
if (relationshipList.size() > 1) {
for (Relationship rel : relationshipList) {
if (rel instanceof MapRelationship && rel.getName() != null && rel.getName().equals(linkName)) {
return rel;
}
}
return null;
}
return relationshipList.get(0);
}
@Override
public Relationship getRelationship(String type, String linkName, String fieldName) {
Class<?> clz = schemaFactory.getSchemaClass(type, true);
Map<String, List<Relationship>> relationship = relationshipsBothCase.get(clz);
List<Relationship> relationshipList = relationship.get(linkName);
return getRelationShipField(relationshipList, linkName, fieldName);
}
@Override
public Relationship getRelationship(Class<?> clz, String linkName, String fieldName) {
Map<String, List<Relationship>> relationship = relationshipsBothCase.get(clz);
List<Relationship> relationshipList = relationship.get(linkName);
return getRelationShipField(relationshipList, linkName, fieldName);
}
private Relationship getRelationShipField(List<Relationship> relationshipList, String linkName, String fieldName) {
if (relationshipList == null || relationshipList.size() == 0) {
return null;
}
if (relationshipList.size() > 1) {
for (Relationship rel : relationshipList) {
if (rel instanceof ForeignKeyRelationship && rel.getName() != null
&& rel.getName().equalsIgnoreCase(linkName)
&& rel.getPropertyName().equalsIgnoreCase(fieldName)) {
return rel;
}
}
return null;
}
return relationshipList.get(0);
}
@Override
public Map<String, Object> getTransitionFields(Schema schema, Object obj) {
Set<String> states = transitioningStates.get(schema.getId());
if (states == null || states.size() == 0) {
schema = schemaFactory.getSchema(obj.getClass());
states = transitioningStates.get(schema.getId());
if (states == null) {
return Collections.emptyMap();
}
}
Map<String, Object> result = new LinkedHashMap<String, Object>();
result.put(TRANSITIONING_FIELD, TRANSITIONING_NO);
result.put(TRANSITIONING_MESSAGE_FIELD, null);
result.put(TRANSITIONING_PROGRESS_FIELD, null);
String message = DataAccessor.fieldString(obj, TRANSITIONING_MESSAGE_FIELD);
Integer progress = DataAccessor.fieldInteger(obj, TRANSITIONING_PROGRESS_FIELD);
String state = DataUtils.getState(obj);
if (TRANSITIONING_ERROR_OVERRIDE.equals(DataAccessor.fieldString(obj, TRANSITIONING_FIELD))) {
Map<String, Object> errorResult = new LinkedHashMap<String, Object>();
errorResult.put(TRANSITIONING_FIELD, TRANSITIONING_ERROR);
return errorResult;
} else if (state != null && states.contains(state)) {
result.put(TRANSITIONING_FIELD, TRANSITIONING_YES);
result.put(TRANSITIONING_MESSAGE_FIELD, message == null ? TRANSITIONING_MESSAGE_DEFAULT_FIELD : message);
result.put(TRANSITIONING_PROGRESS_FIELD, progress);
} else if (TRANSITIONING_ERROR.equals(DataAccessor.fieldString(obj, TRANSITIONING_FIELD))) {
return Collections.emptyMap();
}
return result;
}
@Override
public boolean isTransitioningState(Class<?> resourceType, String state) {
Schema schema = schemaFactory.getSchema(resourceType);
Set<String> states = transitioningStates.get(schema.getId());
if (states == null) {
return false;
}
return state != null && states.contains(state);
}
@Override
public Map<String, ActionDefinition> getActionDefinitions(Object obj) {
if (obj == null) {
return null;
}
Schema schema = schemaFactory.getSchema(obj.getClass());
if (schema == null) {
return null;
}
String type = schemaFactory.getBaseType(schema.getId());
return actionDefinitions.get(type);
}
public SchemaFactory getSchemaFactory() {
return schemaFactory;
}
@Inject
@Named("CoreSchemaFactory")
public void setSchemaFactory(SchemaFactory schemaFactory) {
this.schemaFactory = schemaFactory;
}
public List<TypeSet> getTypeSets() {
return typeSets;
}
@Inject
public void setTypeSets(List<TypeSet> typeSets) {
this.typeSets = CollectionUtils.orderList(TypeSet.class, typeSets);
}
public List<ProcessDefinition> getProcessDefinitions() {
return processDefinitions;
}
@Inject
public void setProcessDefinitions(List<ProcessDefinition> processDefinitions) {
this.processDefinitions = processDefinitions;
}
@Override
public int getPriority() {
return Priority.PRE;
}
}