PageRenderTime 43ms CodeModel.GetById 11ms RepoModel.GetById 0ms app.codeStats 0ms

/odata4j-core/src/main/java/org/odata4j/core/OEntityKey.java

https://code.google.com/p/odata4j/
Java | 389 lines | 268 code | 45 blank | 76 comment | 91 complexity | 5c7fe74b416fa52b0f2ca319df9adf3f MD5 | raw file
  1. package org.odata4j.core;
  2. import java.util.ArrayList;
  3. import java.util.Comparator;
  4. import java.util.HashMap;
  5. import java.util.List;
  6. import java.util.Map;
  7. import java.util.Set;
  8. import java.util.SortedSet;
  9. import java.util.TreeSet;
  10. import org.core4j.Enumerable;
  11. import org.core4j.Func1;
  12. import org.odata4j.edm.EdmEntitySet;
  13. import org.odata4j.edm.EdmEntityType;
  14. import org.odata4j.edm.EdmProperty;
  15. import org.odata4j.edm.EdmSimpleType;
  16. import org.odata4j.expression.CommonExpression;
  17. import org.odata4j.expression.Expression;
  18. import org.odata4j.expression.ExpressionParser;
  19. import org.odata4j.expression.LiteralExpression;
  20. /**
  21. * An immutable entity-key, made up of either a single unnamed-value or multiple named-values.
  22. * 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})
  23. * <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>
  24. * <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>
  25. * <p>Entity-keys are equal if their string representations are equal.</p>
  26. */
  27. public class OEntityKey {
  28. /**
  29. * SINGLE or COMPLEX
  30. */
  31. public enum KeyType {
  32. SINGLE,
  33. COMPLEX
  34. }
  35. private final Object[] values;
  36. private final String keyString;
  37. private OEntityKey(Object[] values) {
  38. this.values = values;
  39. this.keyString = keyString(values);
  40. }
  41. /**
  42. * Creates an entity-key.
  43. * <ul>
  44. * <li><code>OEntityKey.create(2)</code></li>,
  45. * <li><code>OEntityKey.create("foo")</code></li>
  46. * <li><code>OEntityKey.create("a",1,"foo","bar")</code></li>
  47. * <li><code>OEntityKey.create(NamedValues.create("a",1),NamedValues.create("foo","bar"))</code></li>
  48. * </ul></p>
  49. *
  50. * @param values the key values
  51. * @return a newly-created entity-key
  52. */
  53. @SuppressWarnings("unchecked")
  54. public static OEntityKey create(Object... values) {
  55. if (values != null && values.length == 1 && values[0] instanceof Iterable<?>)
  56. return create(Enumerable.create((Iterable<Object>) values[0]).toArray(Object.class));
  57. if (values != null && values.length == 1 && values[0] instanceof OEntityKey)
  58. return (OEntityKey) values[0];
  59. if (values != null && values.length == 1 && values[0] instanceof OEntityId)
  60. return ((OEntityId) values[0]).getEntityKey();
  61. if (values != null && values.length > 1 && values.length % 2 == 0 && values[0] instanceof String) {
  62. Map<String, Object> rt = new HashMap<String, Object>();
  63. for (int i = 0; i < values.length; i += 2) {
  64. String name = (String) values[i];
  65. Object value = values[i + 1];
  66. rt.put(name, value);
  67. }
  68. return create(rt);
  69. }
  70. Object[] v = validate(values);
  71. return new OEntityKey(v);
  72. }
  73. /**
  74. * Creates an entity-key from a map of names and values.
  75. *
  76. * @param values the map of names and values
  77. * @return a newly-created entity-key
  78. */
  79. public static OEntityKey create(Map<String, Object> values) {
  80. return create(NamedValues.fromMap(values));
  81. }
  82. /**
  83. * Creates an entity-key using key information from the given entity-set and values from the given property list.
  84. *
  85. * @param entitySet an entity-set to provide key information
  86. * @param props a list of properties to provide key values
  87. * @return a newly-created entity-key
  88. */
  89. public static OEntityKey infer(EdmEntitySet entitySet, List<OProperty<?>> props) {
  90. if (entitySet == null)
  91. throw new IllegalArgumentException("EdmEntitySet cannot be null");
  92. if (props == null)
  93. throw new IllegalArgumentException("props cannot be null");
  94. EdmEntityType eet = entitySet.getType();
  95. if (eet == null)
  96. throw new IllegalArgumentException("EdmEntityType cannot be null");
  97. List<String> keys = eet.getKeys();
  98. if (keys.size() == 0) {
  99. String idProp = Enumerable.create(eet.getProperties())
  100. .select(OFuncs.name(EdmProperty.class))
  101. .firstOrNull(OPredicates.equalsIgnoreCase("id"));
  102. if (idProp != null)
  103. keys.add(idProp);
  104. }
  105. Object[] v = new Object[keys.size()];
  106. for (int i = 0; i < v.length; i++) {
  107. String keyPropertyName = keys.get(i);
  108. v[i] = getProp(props, keyPropertyName);
  109. }
  110. v = validate(v);
  111. return new OEntityKey(v);
  112. }
  113. /**
  114. * Creates an entity-key from its standard string representation.
  115. *
  116. * @param keyString a standard key-string
  117. * @return a newly-created entity-key
  118. */
  119. public static OEntityKey parse(String keyString) {
  120. if (keyString == null)
  121. throw new IllegalArgumentException("keyString cannot be null");
  122. keyString = keyString.trim();
  123. if (keyString.startsWith("(") && keyString.endsWith(")"))
  124. keyString = keyString.substring(1, keyString.length() - 1);
  125. keyString = keyString.trim();
  126. if (keyString.length() == 0)
  127. throw new IllegalArgumentException("keyString cannot be blank");
  128. String[] tokens = tokens(keyString, ',');
  129. List<Object> values = new ArrayList<Object>(tokens.length);
  130. for (String token : tokens) {
  131. String[] nv = tokens(token, '=');
  132. if (nv.length != 1 && nv.length != 2)
  133. throw new IllegalArgumentException("bad keyString: " + keyString);
  134. String valueString = nv.length == 1 ? nv[0] : nv[1];
  135. try {
  136. CommonExpression expr = ExpressionParser.parse(valueString);
  137. LiteralExpression literal = (LiteralExpression) expr;
  138. Object value = Expression.literalValue(literal);
  139. values.add(nv.length == 1 ? value : NamedValues.create(nv[0], value));
  140. } catch (Exception e) {
  141. throw new IllegalArgumentException(String.format("bad valueString [%s] as part of keyString [%s]", valueString, keyString), e);
  142. }
  143. }
  144. return OEntityKey.create(values);
  145. }
  146. private static String[] tokens(String ks, char sep) {
  147. List<String> rt = new ArrayList<String>();
  148. boolean inString = false;
  149. int start = 0;
  150. for (int i = 0; i < ks.length(); i++) {
  151. char c = ks.charAt(i);
  152. if (c == sep && !inString) {
  153. rt.add(ks.substring(start, i));
  154. start = i + 1;
  155. continue;
  156. }
  157. if (c == '\'') {
  158. if (!inString) {
  159. inString = true;
  160. continue;
  161. }
  162. if (i < ks.length() - 1) {
  163. char next = ks.charAt(i + 1);
  164. if (next == '\'') {
  165. i++;
  166. continue;
  167. }
  168. }
  169. inString = false;
  170. }
  171. }
  172. rt.add(ks.substring(start, ks.length()));
  173. return rt.toArray(new String[rt.size()]);
  174. }
  175. @Override
  176. public String toString() {
  177. return toKeyString();
  178. }
  179. /**
  180. * Gets the standard string representation of this entity-key, including parentheses.
  181. *
  182. * @return the standard key-string
  183. */
  184. public String toKeyString() {
  185. return keyString;
  186. }
  187. /**
  188. * Gets the standard string representation of this entity-key, excluding parentheses.
  189. *
  190. * @return the standard key-string, without parentheses
  191. */
  192. public String toKeyStringWithoutParentheses() {
  193. String keyString = this.keyString;
  194. if (keyString.startsWith("(") && keyString.endsWith(")"))
  195. keyString = keyString.substring(1, keyString.length() - 1);
  196. return keyString;
  197. }
  198. @Override
  199. public int hashCode() {
  200. return keyString.hashCode();
  201. }
  202. @Override
  203. public boolean equals(Object obj) {
  204. return (obj instanceof OEntityKey)
  205. && ((OEntityKey) obj).keyString.equals(keyString);
  206. }
  207. /**
  208. * Gets a the value of a single-valued entity-key.
  209. *
  210. * @return the key value
  211. */
  212. public Object asSingleValue() {
  213. if (values.length > 1)
  214. throw new RuntimeException("Complex key cannot be represented as a single value");
  215. return values[0];
  216. }
  217. /**
  218. * Gets the values of a complex entity-key.
  219. *
  220. * @return the key values as a set of named-values
  221. */
  222. @SuppressWarnings("unchecked")
  223. public Set<NamedValue<?>> asComplexValue() {
  224. assertComplex();
  225. return (Set<NamedValue<?>>) (Object) toSortedSet(Enumerable.create(values).cast(NamedValue.class), OComparators.namedValueByNameRaw());
  226. }
  227. /**
  228. * Gets the values of complex entity-key.
  229. *
  230. * @return the key values as a set of properties
  231. */
  232. public Set<OProperty<?>> asComplexProperties() {
  233. assertComplex();
  234. return toSortedSet(Enumerable.create(values).cast(NamedValue.class).select(OFuncs.namedValueToPropertyRaw()), OComparators.propertyByName());
  235. }
  236. /**
  237. * returns the value for a named value in a complex key.
  238. * @param name
  239. * @return the value
  240. */
  241. public Object getComplexKeyValue(String name) {
  242. assertComplex();
  243. for (Object o : this.values) {
  244. NamedValue<?> nv = (NamedValue<?>) o;
  245. if (nv.getName().equals(name)) {
  246. return nv.getValue();
  247. }
  248. }
  249. return null;
  250. }
  251. private static <T> SortedSet<T> toSortedSet(Enumerable<T> enumerable, Comparator<T> comparator) {
  252. TreeSet<T> rt = new TreeSet<T>(comparator);
  253. rt.addAll(enumerable.toSet());
  254. return rt;
  255. }
  256. private void assertComplex() {
  257. if (values.length == 1)
  258. throw new RuntimeException("Single-valued key cannot be represented as a complex value");
  259. }
  260. /**
  261. * Gets the entity-key type: SINGLE or COMPLEX.
  262. *
  263. * @return SINGLE or COMPLEX
  264. */
  265. public KeyType getKeyType() {
  266. return values.length == 1 ? KeyType.SINGLE : KeyType.COMPLEX;
  267. }
  268. private static OProperty<?> getProp(List<OProperty<?>> props, String name) {
  269. for (OProperty<?> prop : props)
  270. if (prop.getName().equals(name))
  271. return prop;
  272. throw new IllegalArgumentException(String.format(
  273. "Property %s not found in %s", name, props));
  274. }
  275. private static Object[] validate(Object[] values) {
  276. if (values == null)
  277. throw new IllegalArgumentException("Key values cannot be null");
  278. for (Object value : values)
  279. if (value == null)
  280. throw new IllegalArgumentException("Key values cannot be null");
  281. if (values.length == 0)
  282. throw new IllegalArgumentException("Key values cannot be empty");
  283. if (values.length == 1) {
  284. Object o = values[0];
  285. if (o instanceof NamedValue)
  286. o = ((NamedValue<?>) o).getValue();
  287. assertSimple(o);
  288. return new Object[] { o };
  289. }
  290. Object[] v = new Object[values.length];
  291. for (int i = 0; i < values.length; i++) {
  292. Object o = values[i];
  293. if (!(o instanceof NamedValue<?>))
  294. throw new IllegalArgumentException(
  295. "Complex key values must be named");
  296. NamedValue<?> nv = (NamedValue<?>) o;
  297. if (nv.getName() == null || nv.getName().length() == 0)
  298. throw new IllegalArgumentException(
  299. "Complex key values must be named");
  300. if (nv.getValue() == null)
  301. throw new IllegalArgumentException(
  302. "Complex key values cannot be null");
  303. assertSimple(nv.getValue());
  304. v[i] = NamedValues.copy(nv);
  305. }
  306. return v;
  307. }
  308. private static void assertSimple(Object o) {
  309. if (!EDM_SIMPLE_JAVA_TYPES.contains(o.getClass()))
  310. throw new IllegalArgumentException("Key value must be a simple type, found: " + o.getClass().getName());
  311. }
  312. private static final Set<Class<?>> EDM_SIMPLE_JAVA_TYPES =
  313. Enumerable.create(EdmSimpleType.ALL).selectMany(new Func1<EdmSimpleType<?>, Enumerable<Class<?>>>() {
  314. public Enumerable<Class<?>> apply(EdmSimpleType<?> input) {
  315. return Enumerable.create(input.getJavaTypes());
  316. }
  317. }).toSet();
  318. private static String keyString(Object[] values) {
  319. String keyValue;
  320. if (values.length == 1) {
  321. keyValue = keyString(values[0], false);
  322. } else {
  323. keyValue = Enumerable.create(values)
  324. .select(new Func1<Object, String>() {
  325. public String apply(Object input) {
  326. return keyString(input, true);
  327. }
  328. }).orderBy().join(",");
  329. }
  330. return "(" + keyValue + ")";
  331. }
  332. private static String keyString(Object keyValue, boolean includePropName) {
  333. if (keyValue instanceof NamedValue<?>) {
  334. NamedValue<?> namedValue = (NamedValue<?>) keyValue;
  335. String value = keyString(namedValue.getValue(), false);
  336. if (includePropName)
  337. return namedValue.getName() + "=" + value;
  338. else
  339. return value;
  340. }
  341. LiteralExpression expr = Expression.literal(keyValue);
  342. return Expression.asFilterString(expr);
  343. }
  344. }