/core/src/main/php/webservices/rest/RestDeserializer.class.php

https://github.com/treuter/xp-framework · PHP · 175 lines · 116 code · 9 blank · 50 comment · 49 complexity · c05cdf2f310b7b890b221e49bf61d15e MD5 · raw file

  1. <?php
  2. /* This class is part of the XP framework
  3. *
  4. * $Id$
  5. */
  6. uses('webservices.rest.Payload');
  7. /**
  8. * Deserializer abstract base class
  9. *
  10. */
  11. abstract class RestDeserializer extends Object {
  12. /**
  13. * Calculate variants of a given name
  14. *
  15. * @param string name
  16. * @return string[] names
  17. */
  18. protected function variantsOf($name) {
  19. $variants= array($name);
  20. $chunks= explode('_', $name);
  21. if (sizeof($chunks) > 1) { // product_id => productId
  22. $variants[]= array_shift($chunks).implode(array_map('ucfirst', $chunks));
  23. }
  24. return $variants;
  25. }
  26. /**
  27. * Returns the first element of a given traversable data structure
  28. * or the data structure itself, or NULL if the structure has more
  29. * than one element.
  30. *
  31. * @param var struct
  32. * @param var[]
  33. */
  34. protected function keyOf($struct) {
  35. if (is_array($struct) || $struct instanceof Traversable) {
  36. $return= NULL;
  37. foreach ($struct as $element) {
  38. if (NULL === $return) {
  39. $return= array($element);
  40. continue;
  41. }
  42. return NULL; // Found a second element, return NULL
  43. }
  44. return $return; // Will be NULL if we have no elements
  45. }
  46. return array($struct);
  47. }
  48. /**
  49. * Returns the first element of a given traversable data structure
  50. * or the data structure itself
  51. *
  52. * @param var struct
  53. * @param var
  54. */
  55. protected function valueOf($struct) {
  56. if (is_array($struct) || $struct instanceof Traversable) {
  57. foreach ($struct as $element) {
  58. return $element;
  59. }
  60. }
  61. return $struct;
  62. }
  63. /**
  64. * Convert data based on type
  65. *
  66. * @param lang.Type type
  67. * @param [:var] data
  68. * @return var
  69. */
  70. public function convert($type, $data) {
  71. if (NULL === $type || $type->equals(Type::$VAR)) { // No conversion
  72. return $data;
  73. } else if (NULL === $data) { // Valid for any type
  74. return NULL;
  75. } else if ($type->equals(XPClass::forName('lang.types.String'))) {
  76. return new String($this->valueOf($data));
  77. } else if ($type->equals(XPClass::forName('util.Date'))) {
  78. return $type->newInstance($data);
  79. } else if ($type instanceof XPClass) {
  80. // Check if a public static one-arg valueOf() method exists
  81. // E.g.: Assuming the target type has a valueOf(string $id) and the
  82. // given payload data is either a map or an array with one element, or
  83. // a primitive, then pass that as value. Examples: { "id" : "4711" },
  84. // [ "4711" ] or "4711" - in all cases pass just "4711".
  85. if ($type->hasMethod('valueOf')) {
  86. $m= $type->getMethod('valueOf');
  87. if (Modifiers::isStatic($m->getModifiers()) && Modifiers::isPublic($m->getModifiers()) && 1 === $m->numParameters()) {
  88. if (NULL !== ($arg= $this->keyOf($data))) {
  89. return $m->invoke(NULL, array($this->convert($m->getParameter(0)->getType(), $arg[0])));
  90. }
  91. }
  92. }
  93. // Generic approach
  94. $return= $type->newInstance();
  95. if (NULL === $data) {
  96. $iter= array();
  97. } else if (is_array($data) || $data instanceof Traversable) {
  98. $iter= $data;
  99. } else {
  100. $iter= array($data);
  101. }
  102. foreach ($iter as $name => $value) {
  103. foreach ($this->variantsOf($name) as $variant) {
  104. if ($type->hasField($variant)) {
  105. $field= $type->getField($variant);
  106. $m= $field->getModifiers();
  107. if ($m & MODIFIER_STATIC) {
  108. continue;
  109. } else if ($m & MODIFIER_PUBLIC) {
  110. if (NULL !== ($fType= $field->getType())) {
  111. $field->set($return, $this->convert($fType, $value));
  112. } else {
  113. $field->set($return, $value);
  114. }
  115. continue 2;
  116. }
  117. }
  118. if ($type->hasMethod('set'.$variant)) {
  119. $method= $type->getMethod('set'.$variant);
  120. if ($method->getModifiers() & MODIFIER_PUBLIC) {
  121. if (NULL !== ($param= $method->getParameter(0))) {
  122. $method->invoke($return, array($this->convert($param->getType(), $value)));
  123. } else {
  124. $method->invoke($return, array($value));
  125. }
  126. continue 2;
  127. }
  128. }
  129. }
  130. }
  131. return $return;
  132. } else if ($type instanceof ArrayType) {
  133. $return= array();
  134. foreach ($data as $element) {
  135. $return[]= $this->convert($type->componentType(), $element);
  136. }
  137. return $return;
  138. } else if ($type instanceof MapType) {
  139. $return= array();
  140. foreach ($data as $key => $element) {
  141. $return[$key]= $this->convert($type->componentType(), $element);
  142. }
  143. return $return;
  144. } else if ($type->equals(Primitive::$STRING)) {
  145. return (string)$this->valueOf($data);
  146. } else if ($type->equals(Primitive::$INT)) {
  147. return (int)$this->valueOf($data);
  148. } else if ($type->equals(Primitive::$DOUBLE)) {
  149. return (double)$this->valueOf($data);
  150. } else if ($type->equals(Primitive::$BOOL)) {
  151. return (bool)$this->valueOf($data);
  152. } else {
  153. throw new FormatException('Cannot convert to '.xp::stringOf($type));
  154. }
  155. }
  156. /**
  157. * Deserialize
  158. *
  159. * @param io.streams.InputStream in
  160. * @param lang.Type target
  161. * @return var
  162. * @throws lang.FormatException
  163. */
  164. public abstract function deserialize($in, $target);
  165. }
  166. ?>