/odata4j-core/src/main/java/org/odata4j/core/OEntityKey.java
Java | 389 lines | 268 code | 45 blank | 76 comment | 91 complexity | 5c7fe74b416fa52b0f2ca319df9adf3f MD5 | raw file
- package org.odata4j.core;
-
- import java.util.ArrayList;
- import java.util.Comparator;
- import java.util.HashMap;
- import java.util.List;
- import java.util.Map;
- import java.util.Set;
- import java.util.SortedSet;
- import java.util.TreeSet;
-
- import org.core4j.Enumerable;
- import org.core4j.Func1;
- import org.odata4j.edm.EdmEntitySet;
- import org.odata4j.edm.EdmEntityType;
- import org.odata4j.edm.EdmProperty;
- import org.odata4j.edm.EdmSimpleType;
- import org.odata4j.expression.CommonExpression;
- import org.odata4j.expression.Expression;
- import org.odata4j.expression.ExpressionParser;
- import org.odata4j.expression.LiteralExpression;
-
- /**
- * An immutable entity-key, made up of either a single unnamed-value or multiple named-values.
- * Every entity must have an entity-key. The entity-key must be unique within the entity-set, and thus defines an entity's identity. (see {@link OEntityId})
- * <p>An entity-key made up a a single unnamed-value is called a single key. An entity-key made up of multiple named-values is called a complex key.</p>
- * <p>The string representation of an entity-key is wrapped with parentheses, such as <code>(2)</code>, <code>('foo')</code> or <code>(a=1,foo='bar')</code>.</p>
- * <p>Entity-keys are equal if their string representations are equal.</p>
- */
- public class OEntityKey {
-
- /**
- * SINGLE or COMPLEX
- */
- public enum KeyType {
- SINGLE,
- COMPLEX
- }
-
- private final Object[] values;
- private final String keyString;
-
- private OEntityKey(Object[] values) {
- this.values = values;
- this.keyString = keyString(values);
- }
-
- /**
- * Creates an entity-key.
- * <ul>
- * <li><code>OEntityKey.create(2)</code></li>,
- * <li><code>OEntityKey.create("foo")</code></li>
- * <li><code>OEntityKey.create("a",1,"foo","bar")</code></li>
- * <li><code>OEntityKey.create(NamedValues.create("a",1),NamedValues.create("foo","bar"))</code></li>
- * </ul></p>
- *
- * @param values the key values
- * @return a newly-created entity-key
- */
- @SuppressWarnings("unchecked")
- public static OEntityKey create(Object... values) {
- if (values != null && values.length == 1 && values[0] instanceof Iterable<?>)
- return create(Enumerable.create((Iterable<Object>) values[0]).toArray(Object.class));
- if (values != null && values.length == 1 && values[0] instanceof OEntityKey)
- return (OEntityKey) values[0];
- if (values != null && values.length == 1 && values[0] instanceof OEntityId)
- return ((OEntityId) values[0]).getEntityKey();
- if (values != null && values.length > 1 && values.length % 2 == 0 && values[0] instanceof String) {
- Map<String, Object> rt = new HashMap<String, Object>();
- for (int i = 0; i < values.length; i += 2) {
- String name = (String) values[i];
- Object value = values[i + 1];
- rt.put(name, value);
- }
- return create(rt);
- }
- Object[] v = validate(values);
- return new OEntityKey(v);
- }
-
- /**
- * Creates an entity-key from a map of names and values.
- *
- * @param values the map of names and values
- * @return a newly-created entity-key
- */
- public static OEntityKey create(Map<String, Object> values) {
- return create(NamedValues.fromMap(values));
- }
-
- /**
- * Creates an entity-key using key information from the given entity-set and values from the given property list.
- *
- * @param entitySet an entity-set to provide key information
- * @param props a list of properties to provide key values
- * @return a newly-created entity-key
- */
- public static OEntityKey infer(EdmEntitySet entitySet, List<OProperty<?>> props) {
- if (entitySet == null)
- throw new IllegalArgumentException("EdmEntitySet cannot be null");
- if (props == null)
- throw new IllegalArgumentException("props cannot be null");
- EdmEntityType eet = entitySet.getType();
- if (eet == null)
- throw new IllegalArgumentException("EdmEntityType cannot be null");
- List<String> keys = eet.getKeys();
- if (keys.size() == 0) {
- String idProp = Enumerable.create(eet.getProperties())
- .select(OFuncs.name(EdmProperty.class))
- .firstOrNull(OPredicates.equalsIgnoreCase("id"));
- if (idProp != null)
- keys.add(idProp);
- }
- Object[] v = new Object[keys.size()];
- for (int i = 0; i < v.length; i++) {
- String keyPropertyName = keys.get(i);
- v[i] = getProp(props, keyPropertyName);
- }
- v = validate(v);
- return new OEntityKey(v);
- }
-
- /**
- * Creates an entity-key from its standard string representation.
- *
- * @param keyString a standard key-string
- * @return a newly-created entity-key
- */
- public static OEntityKey parse(String keyString) {
-
- if (keyString == null)
- throw new IllegalArgumentException("keyString cannot be null");
-
- keyString = keyString.trim();
-
- if (keyString.startsWith("(") && keyString.endsWith(")"))
- keyString = keyString.substring(1, keyString.length() - 1);
-
- keyString = keyString.trim();
-
- if (keyString.length() == 0)
- throw new IllegalArgumentException("keyString cannot be blank");
-
- String[] tokens = tokens(keyString, ',');
- List<Object> values = new ArrayList<Object>(tokens.length);
- for (String token : tokens) {
- String[] nv = tokens(token, '=');
- if (nv.length != 1 && nv.length != 2)
- throw new IllegalArgumentException("bad keyString: " + keyString);
- String valueString = nv.length == 1 ? nv[0] : nv[1];
- try {
- CommonExpression expr = ExpressionParser.parse(valueString);
- LiteralExpression literal = (LiteralExpression) expr;
- Object value = Expression.literalValue(literal);
- values.add(nv.length == 1 ? value : NamedValues.create(nv[0], value));
- } catch (Exception e) {
- throw new IllegalArgumentException(String.format("bad valueString [%s] as part of keyString [%s]", valueString, keyString), e);
- }
- }
- return OEntityKey.create(values);
- }
-
- private static String[] tokens(String ks, char sep) {
- List<String> rt = new ArrayList<String>();
- boolean inString = false;
- int start = 0;
- for (int i = 0; i < ks.length(); i++) {
- char c = ks.charAt(i);
- if (c == sep && !inString) {
- rt.add(ks.substring(start, i));
- start = i + 1;
- continue;
- }
- if (c == '\'') {
- if (!inString) {
- inString = true;
- continue;
- }
-
- if (i < ks.length() - 1) {
- char next = ks.charAt(i + 1);
- if (next == '\'') {
- i++;
- continue;
- }
-
- }
- inString = false;
- }
- }
- rt.add(ks.substring(start, ks.length()));
- return rt.toArray(new String[rt.size()]);
- }
-
- @Override
- public String toString() {
- return toKeyString();
- }
-
- /**
- * Gets the standard string representation of this entity-key, including parentheses.
- *
- * @return the standard key-string
- */
- public String toKeyString() {
- return keyString;
- }
-
- /**
- * Gets the standard string representation of this entity-key, excluding parentheses.
- *
- * @return the standard key-string, without parentheses
- */
- public String toKeyStringWithoutParentheses() {
- String keyString = this.keyString;
- if (keyString.startsWith("(") && keyString.endsWith(")"))
- keyString = keyString.substring(1, keyString.length() - 1);
- return keyString;
- }
-
- @Override
- public int hashCode() {
- return keyString.hashCode();
- }
-
- @Override
- public boolean equals(Object obj) {
- return (obj instanceof OEntityKey)
- && ((OEntityKey) obj).keyString.equals(keyString);
- }
-
- /**
- * Gets a the value of a single-valued entity-key.
- *
- * @return the key value
- */
- public Object asSingleValue() {
- if (values.length > 1)
- throw new RuntimeException("Complex key cannot be represented as a single value");
- return values[0];
- }
-
- /**
- * Gets the values of a complex entity-key.
- *
- * @return the key values as a set of named-values
- */
- @SuppressWarnings("unchecked")
- public Set<NamedValue<?>> asComplexValue() {
- assertComplex();
- return (Set<NamedValue<?>>) (Object) toSortedSet(Enumerable.create(values).cast(NamedValue.class), OComparators.namedValueByNameRaw());
- }
-
- /**
- * Gets the values of complex entity-key.
- *
- * @return the key values as a set of properties
- */
- public Set<OProperty<?>> asComplexProperties() {
- assertComplex();
- return toSortedSet(Enumerable.create(values).cast(NamedValue.class).select(OFuncs.namedValueToPropertyRaw()), OComparators.propertyByName());
- }
-
- /**
- * returns the value for a named value in a complex key.
- * @param name
- * @return the value
- */
- public Object getComplexKeyValue(String name) {
- assertComplex();
- for (Object o : this.values) {
- NamedValue<?> nv = (NamedValue<?>) o;
- if (nv.getName().equals(name)) {
- return nv.getValue();
- }
- }
- return null;
- }
-
- private static <T> SortedSet<T> toSortedSet(Enumerable<T> enumerable, Comparator<T> comparator) {
- TreeSet<T> rt = new TreeSet<T>(comparator);
- rt.addAll(enumerable.toSet());
- return rt;
- }
-
- private void assertComplex() {
- if (values.length == 1)
- throw new RuntimeException("Single-valued key cannot be represented as a complex value");
- }
-
- /**
- * Gets the entity-key type: SINGLE or COMPLEX.
- *
- * @return SINGLE or COMPLEX
- */
- public KeyType getKeyType() {
- return values.length == 1 ? KeyType.SINGLE : KeyType.COMPLEX;
- }
-
- private static OProperty<?> getProp(List<OProperty<?>> props, String name) {
- for (OProperty<?> prop : props)
- if (prop.getName().equals(name))
- return prop;
- throw new IllegalArgumentException(String.format(
- "Property %s not found in %s", name, props));
-
- }
-
- private static Object[] validate(Object[] values) {
- if (values == null)
- throw new IllegalArgumentException("Key values cannot be null");
- for (Object value : values)
- if (value == null)
- throw new IllegalArgumentException("Key values cannot be null");
- if (values.length == 0)
- throw new IllegalArgumentException("Key values cannot be empty");
-
- if (values.length == 1) {
- Object o = values[0];
- if (o instanceof NamedValue)
- o = ((NamedValue<?>) o).getValue();
- assertSimple(o);
- return new Object[] { o };
- }
-
- Object[] v = new Object[values.length];
- for (int i = 0; i < values.length; i++) {
- Object o = values[i];
- if (!(o instanceof NamedValue<?>))
- throw new IllegalArgumentException(
- "Complex key values must be named");
- NamedValue<?> nv = (NamedValue<?>) o;
- if (nv.getName() == null || nv.getName().length() == 0)
- throw new IllegalArgumentException(
- "Complex key values must be named");
- if (nv.getValue() == null)
- throw new IllegalArgumentException(
- "Complex key values cannot be null");
- assertSimple(nv.getValue());
- v[i] = NamedValues.copy(nv);
- }
- return v;
-
- }
-
- private static void assertSimple(Object o) {
- if (!EDM_SIMPLE_JAVA_TYPES.contains(o.getClass()))
- throw new IllegalArgumentException("Key value must be a simple type, found: " + o.getClass().getName());
- }
-
- private static final Set<Class<?>> EDM_SIMPLE_JAVA_TYPES =
- Enumerable.create(EdmSimpleType.ALL).selectMany(new Func1<EdmSimpleType<?>, Enumerable<Class<?>>>() {
- public Enumerable<Class<?>> apply(EdmSimpleType<?> input) {
- return Enumerable.create(input.getJavaTypes());
- }
- }).toSet();
-
- private static String keyString(Object[] values) {
-
- String keyValue;
- if (values.length == 1) {
- keyValue = keyString(values[0], false);
- } else {
- keyValue = Enumerable.create(values)
- .select(new Func1<Object, String>() {
- public String apply(Object input) {
- return keyString(input, true);
- }
- }).orderBy().join(",");
- }
-
- return "(" + keyValue + ")";
- }
-
- private static String keyString(Object keyValue, boolean includePropName) {
- if (keyValue instanceof NamedValue<?>) {
- NamedValue<?> namedValue = (NamedValue<?>) keyValue;
- String value = keyString(namedValue.getValue(), false);
- if (includePropName)
- return namedValue.getName() + "=" + value;
- else
- return value;
- }
-
- LiteralExpression expr = Expression.literal(keyValue);
- return Expression.asFilterString(expr);
- }
-
- }