package org.odata4j.edm;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import org.core4j.Enumerable;
import org.core4j.Func;
import org.core4j.Func1;
import org.core4j.Predicate1;
import org.odata4j.core.ImmutableList;
import org.odata4j.core.Named;
import org.odata4j.core.OPredicates;
/**
* A CSDL EntityType element.
*
* <p>The EntityType element represents the structure of a top-level concept, such as a customer or order,
* in a conceptual model. An entity type is a template for instances of entity types in an application.
* Each template contains the following information:
* <li>A unique name. (Required.)
* <li>An entity key that is defined by one or more properties. (Required.)
* <li>Properties for containing data. (Optional.)
* <li>Navigation properties that allow for navigation from one end of an association to the other end. (Optional.)
*
* <p>In an application, an instance of an entity type represents a specific object (such as a specific customer or order).
* Each instance of an entity type must have a unique entity key within an entity set.
*
* <p>Two entity type instances are considered equal only if they are of the same type and the values of their entity keys are the same.
*
* @see <a href="http://msdn.microsoft.com/en-us/library/bb399206.aspx">[msdn] EntityType Element (CSDL)</a>
*/
public class EdmEntityType extends EdmStructuralType {
private final String alias;
private final Boolean hasStream;
private final Boolean openType;
private final List<String> keys;
private final List<EdmNavigationProperty> navigationProperties;
private EdmEntityType(String namespace, String alias, String name, Boolean hasStream, Boolean openType,
ImmutableList<String> keys, EdmEntityType baseType, List<EdmProperty.Builder> properties,
ImmutableList<EdmNavigationProperty> navigationProperties, EdmDocumentation doc,
ImmutableList<EdmAnnotation<?>> annotations, ImmutableList<EdmAnnotation<?>> annotElements, Boolean isAbstract) {
super(baseType, namespace, name, properties, doc, annotations, annotElements, isAbstract);
this.alias = alias;
this.hasStream = hasStream;
this.openType = openType;
this.keys = (keys == null || keys.isEmpty()) ?
(baseType == null ? findConventionalKeys() : null) :
keys;
if (baseType == null && this.keys == null)
throw new IllegalArgumentException("Root types must have keys");
if (baseType != null && this.keys != null)
throw new IllegalArgumentException("Keys on root types only");
this.navigationProperties = navigationProperties;
}
private List<String> findConventionalKeys() {
for (EdmProperty prop : getProperties()) {
if (prop.getName().equalsIgnoreCase("Id") && prop.getType().isSimple() && !prop.isNullable()) {
return Enumerable.create(prop.getName()).toList();
}
}
return null;
}
public String getAlias() {
return alias;
}
public Boolean getHasStream() {
return hasStream != null ? hasStream : false;
}
public Boolean getOpenType() {
return openType;
}
public String getFQAliasName() {
return alias == null ? null : (alias + "." + getName());
}
@Override
public String toString() {
return String.format("EdmEntityType[%s.%s,alias=%s]", getNamespace(), getName(), alias);
}
/**
* Finds a navigation property by name, searching up the type hierarchy if necessary.
*/
public EdmNavigationProperty findNavigationProperty(String name) {
return getNavigationProperties().firstOrNull(OPredicates.nameEquals(EdmNavigationProperty.class, name));
}
/**
* Gets the navigation properties defined for this entity type <i>not including</i> inherited properties.
*/
public Enumerable<EdmNavigationProperty> getDeclaredNavigationProperties() {
return Enumerable.create(navigationProperties);
}
/**
* Finds a navigation property by name on this entity type <i>not including</i> inherited properties.
*/
public EdmNavigationProperty findDeclaredNavigationProperty(String name) {
return getDeclaredNavigationProperties().firstOrNull(OPredicates.nameEquals(EdmNavigationProperty.class, name));
}
/**
* Gets the navigation properties defined for this entity type <i>including</i> inherited properties.
*/
public Enumerable<EdmNavigationProperty> getNavigationProperties() {
return isRootType()
? getDeclaredNavigationProperties()
: getBaseType().getNavigationProperties().union(getDeclaredNavigationProperties());
}
/**
* Gets the keys for this EdmEntityType. Keys are defined only in a root types.
*/
public List<String> getKeys() {
return isRootType() ? keys : getBaseType().getKeys();
}
public static Builder newBuilder() {
return new Builder();
}
public static Builder newBuilder(EdmEntityType entityType, BuilderContext context) {
return context.newBuilder(entityType, new Builder());
}
/** Mutable builder for {@link EdmEntityType} objects. */
public static class Builder extends EdmStructuralType.Builder<EdmEntityType, Builder> implements Named {
private String alias;
private Boolean hasStream;
private Boolean openType;
private final List<String> keys = new ArrayList<String>();
private final List<EdmNavigationProperty.Builder> navigationProperties = new ArrayList<EdmNavigationProperty.Builder>();
private EdmEntityType.Builder baseTypeBuilder;
private String baseTypeNameFQ;
@Override
Builder newBuilder(EdmEntityType entityType, BuilderContext context) {
fillBuilder(entityType, context);
context.register(entityType, this);
this.alias = entityType.alias;
this.hasStream = entityType.hasStream;
this.openType = entityType.openType;
if (entityType.keys != null) {
// subtypes don't have keys!
this.keys.addAll(entityType.keys);
}
if (entityType.getBaseType() != null) {
baseTypeBuilder = EdmEntityType.newBuilder(entityType.getBaseType(), context);
}
for (EdmNavigationProperty navigationProperty : entityType.navigationProperties)
this.navigationProperties.add(EdmNavigationProperty.newBuilder(navigationProperty, context));
return this;
}
@Override
public EdmEntityType build() {
return (EdmEntityType) _build();
}
@Override
protected EdmEntityType buildImpl() {
List<EdmNavigationProperty> builtNavProps = new ArrayList<EdmNavigationProperty>();
for (EdmNavigationProperty.Builder navigationProperty : this.navigationProperties) {
builtNavProps.add(navigationProperty.build());
}
return new EdmEntityType(namespace, alias, name, hasStream, openType, ImmutableList.copyOf(keys),
(EdmEntityType) (this.baseTypeBuilder != null ? this.baseTypeBuilder.build() : null),
properties, ImmutableList.copyOf(builtNavProps), getDocumentation(),
ImmutableList.copyOf(getAnnotations()), ImmutableList.copyOf(getAnnotationElements()), isAbstract);
}
public Builder addNavigationProperties(EdmNavigationProperty.Builder... navigationProperties) {
return addNavigationProperties(Arrays.asList(navigationProperties));
}
public Builder addNavigationProperties(List<EdmNavigationProperty.Builder> navProperties) {
this.navigationProperties.addAll(navProperties);
return this;
}
public Builder addKeys(List<String> keys) {
this.keys.addAll(keys);
return this;
}
public Builder addKeys(String... keys) {
return addKeys(Arrays.asList(keys));
}
@Override
public String getName() {
return name;
}
public Builder setBaseType(EdmEntityType.Builder baseType) {
this.baseTypeBuilder = baseType;
return this;
}
public Builder setBaseType(String baseTypeName) {
this.baseTypeNameFQ = baseTypeName;
return this;
}
public Builder setAlias(String alias) {
this.alias = alias;
return this;
}
public Builder setHasStream(Boolean hasStream) {
this.hasStream = hasStream;
return this;
}
public Builder setOpenType(Boolean openType) {
this.openType = openType;
return this;
}
public String getAlias() {
return alias;
}
public String getFQAliasName() {
// TODO share or remove
return alias == null ? null : (alias + "." + getName());
}
public String getFQBaseTypeName() {
return baseTypeNameFQ != null
? baseTypeNameFQ
: (baseType != null ? baseType.getFullyQualifiedTypeName() : null);
}
public List<EdmNavigationProperty.Builder> getNavigationProperties() {
return navigationProperties;
}
public Func<EdmEntityType> builtFunc() {
return new Func<EdmEntityType>() {
@Override
public EdmEntityType apply() {
return (EdmEntityType) build();
}
};
}
public static Func1<EdmEntityType.Builder, String> func1_getFullyQualifiedTypeName() {
return new Func1<EdmEntityType.Builder, String>() {
@Override
public String apply(Builder input) {
return input.getFullyQualifiedTypeName();
}
};
}
public static Func1<EdmEntityType.Builder, String> func1_getFQAliasName() {
return new Func1<EdmEntityType.Builder, String>() {
@Override
public String apply(Builder input) {
return input.getFQAliasName();
}
};
}
public static Predicate1<Builder> pred1_hasAlias() {
return new Predicate1<EdmEntityType.Builder>() {
@Override
public boolean apply(Builder input) {
return input.getAlias() != null;
}
};
}
}
}