/**
  *
  * INTERNAL: Performance sensitive method
  *
  * @throws \Doctrine\OXM\Mapping\MappingException
  * @param \XMLReader $cursor
  * @return object
  */
 private function doUnmarshal(XMLReader $cursor, $endElement = NULL, $classMetadata = NULL, $virtualNamespace = NULL)
 {
     /*
     $allMappedXmlNodes = $this->classMetadataFactory->getAllXmlNodes();
     $allMappedWrapperXmlNodes = $this->classMetadataFactory->getAllWrapperXmlNodes();
     */
     static $allMappedXmlNodes, $allMappedWrapperXmlNodes, $alternativeClasses;
     if (empty($allMappedXmlNodes)) {
         list($allMappedXmlNodes, $allMappedWrapperXmlNodes, $alternativeClasses) = $this->classMetadataFactory->getAllMaps();
     }
     if ($cursor->nodeType !== XMLReader::ELEMENT && $cursor->nodeType !== XMLReader::CDATA) {
         throw MarshallerException::invalidMarshallerState($cursor);
     }
     $elementName = $cursor->localName;
     if (!empty($classMetadata)) {
         $isInWrapper = !empty($endElement);
     } else {
         $isInWrapper = FALSE;
         if (!array_key_exists($elementName, $allMappedXmlNodes) && !array_key_exists($elementName, $allMappedWrapperXmlNodes) && !array_key_exists('*', $allMappedXmlNodes)) {
             if ($this->allowUnknownElements) {
                 return $this->doUnmarshalUnknownElement($cursor, $endElement, $classMetadata, $virtualNamespace);
             } else {
                 throw MappingException::invalidMapping($elementName);
             }
         }
         if (array_key_exists($elementName, $allMappedXmlNodes)) {
             $classes = $allMappedXmlNodes[$elementName];
         } else {
             $classes = $allMappedXmlNodes['*'];
         }
         if (count($classes) == 1) {
             $namespaces = array_keys($classes);
             $className = $classes[array_shift($namespaces)];
         } else {
             // Should try to work out the correct namespace version here
             $attributes = $this->getElementAttributes($cursor);
             $attributes['-name'] = $cursor->localName;
             foreach ($classes as $thisClass) {
                 $className = $this->classMetadataFactory->getAlternativeClassForNamespace($thisClass, $virtualNamespace, FALSE);
                 if (!$className) {
                     $className = $this->classMetadataFactory->getAlternativeClassForAttributes($thisClass, $virtualNamespace, $attributes, FALSE);
                 }
                 if (!$className) {
                     break;
                 }
             }
             if (!$className) {
                 $namespaces = array_keys($classes);
                 $className = $classes[array_shift($namespaces)];
                 error_log('Scream: too many choices! Selected ${className} for ${elementName}');
                 error_log(print_r($classes, TRUE));
             }
         }
         $classMetadata = $this->classMetadataFactory->getMetadataFor($className);
     }
     $mappedObject = $classMetadata->newInstance();
     // Pre Unmarshal hook -- doesn't make sense on the wrapper
     if ($classMetadata->hasLifecycleCallbacks(Events::preUnmarshal)) {
         $classMetadata->invokeLifecycleCallbacks(Events::preUnmarshal, $mappedObject);
     }
     if (!$isInWrapper && $cursor->hasAttributes) {
         while ($cursor->moveToNextAttribute()) {
             if ($classMetadata->hasXmlField($cursor->name)) {
                 $fieldName = $classMetadata->getFieldName($cursor->name);
                 $fieldMapping = $classMetadata->getFieldMapping($fieldName);
                 $type = Type::getType($fieldMapping['type']);
                 if ($classMetadata->isRequired($fieldName) && $cursor->value === null) {
                     throw MappingException::fieldRequired($classMetadata->name, $fieldName);
                 }
                 if ($classMetadata->isCollection($fieldName) || $isInWrapper) {
                     $convertedValues = array();
                     foreach (explode(" ", $cursor->value) as $value) {
                         $convertedValues[] = $type->convertToPHPValue($value);
                     }
                     $classMetadata->setFieldValue($mappedObject, $fieldName, $convertedValues);
                 } else {
                     $classMetadata->setFieldValue($mappedObject, $fieldName, $type->convertToPHPValue($cursor->value));
                 }
             }
         }
         $cursor->moveToElement();
     }
     if (!$cursor->isEmptyElement) {
         $collectionElements = array();
         while ($cursor->read()) {
             if ($cursor->nodeType === XMLReader::END_ELEMENT && ($cursor->name === $elementName || $isInWrapper && $cursor->name === $endElement)) {
                 // we're at the original element closing node, bug out
                 break;
             }
             if ($cursor->nodeType !== XMLReader::ELEMENT && $cursor->nodeType !== XMLReader::TEXT && $cursor->nodeType !== XMLReader::CDATA) {
                 // skip insignificant elements
                 continue;
             }
             if ($classMetadata->hasXmlField($cursor->localName) || $classMetadata->hasXmlField('*')) {
                 $fieldName = $classMetadata->getFieldName($cursor->localName);
                 $inAnyMode = FALSE;
                 if (!$fieldName) {
                     $fieldName = $classMetadata->getFieldName('*');
                     $inAnyMode = TRUE;
                 }
                 // Check for mapped entity as child, add recursively
                 $fieldMapping = $classMetadata->getFieldMapping($fieldName);
                 if ($this->classMetadataFactory->hasMetadataFor($fieldMapping['type'])) {
                     $namespace = $cursor->namespaceURI ? $cursor->namespaceURI : $virtualNamespace;
                     $childClass = NULL;
                     if ($inAnyMode) {
                         $childClass = $this->classMetadataFactory->getAlternativeClassForNamespace($fieldMapping['type'], $namespace, FALSE);
                         if (!$childClass) {
                             $childClass = $this->classMetadataFactory->getAlternativeClassForName($fieldMapping['type'], $cursor->localName, FALSE);
                         }
                         if (!$childClass) {
                             // Check the global mapping
                             if (!empty($allMappedXmlNodes[$cursor->localName])) {
                                 $childClass = $allMappedXmlNodes[$cursor->localName];
                                 if (is_array($childClass)) {
                                     $childClass = array_shift($childClass);
                                 }
                             }
                         }
                     }
                     if (empty($childClass)) {
                         $attributes = $this->getElementAttributes($cursor);
                         $attributes['-name'] = $cursor->localName;
                         $childClass = $this->classMetadataFactory->getAlternativeClassForAttributes($fieldMapping['type'], $namespace, $attributes, FALSE);
                         if (!$childClass) {
                             $childClass = $this->classMetadataFactory->getAlternativeClassForNamespace($fieldMapping['type'], $namespace);
                         }
                     }
                     $childClassMetadata = $this->classMetadataFactory->getMetadataFor($childClass);
                     if ($classMetadata->hasFieldWrapping($fieldName) && !$isInWrapper) {
                         $cursor->moveToElement();
                         $collectionElements[$fieldName] = $this->doUnmarshal($cursor, $fieldName, $classMetadata, $namespace);
                     } elseif ($classMetadata->isCollection($fieldName) || $isInWrapper) {
                         $collectionElements[$fieldName][] = $this->doUnmarshal($cursor, NULL, $childClassMetadata, $namespace);
                     } else {
                         $classMetadata->setFieldValue($mappedObject, $fieldName, $this->doUnmarshal($cursor, NULL, $childClassMetadata, $namespace));
                     }
                 } elseif ($cursor->isEmptyElement) {
                     // Don't move on just yet!
                     $type = Type::getType($fieldMapping['type']);
                     if ($classMetadata->isCollection($fieldName) || $isInWrapper) {
                         $collectionElements[$fieldName][] = $type->convertToPHPValue($cursor->value);
                     } else {
                         $classMetadata->setFieldValue($mappedObject, $fieldName, $type->convertToPHPValue($cursor->value));
                     }
                     // Still don't move on as the loop will
                 } else {
                     // assume text element (dangerous?)
                     $cursor->read();
                     if (!$cursor->isEmptyElement && $cursor->nodeType !== XMLReader::END_ELEMENT) {
                         if ($cursor->nodeType !== XMLReader::TEXT && $cursor->nodeType !== XMLReader::CDATA) {
                             throw MarshallerException::invalidMarshallerState($cursor);
                         }
                         $type = Type::getType($fieldMapping['type']);
                         if ($classMetadata->isCollection($fieldName) || $isInWrapper) {
                             $collectionElements[$fieldName][] = $type->convertToPHPValue($cursor->value);
                         } else {
                             $classMetadata->setFieldValue($mappedObject, $fieldName, $type->convertToPHPValue($cursor->value));
                         }
                         $cursor->read();
                     }
                 }
             } elseif ($cursor->nodeType === XMLReader::TEXT || $cursor->nodeType === XMLReader::CDATA) {
                 foreach ($classMetadata->getFieldNames() as $fieldName) {
                     if (ClassMetadata::XML_VALUE === $classMetadata->getFieldXmlNode($fieldName)) {
                         $fieldMapping = $classMetadata->getFieldMapping($fieldName);
                         $type = Type::getType($fieldMapping['type']);
                         $classMetadata->setFieldValue($mappedObject, $fieldName, $type->convertToPHPValue($cursor->value));
                     }
                 }
             } elseif (array_key_exists($cursor->name, $allMappedXmlNodes) || array_key_exists($cursor->name, $allMappedWrapperXmlNodes) && isset($allMappedWrapperXmlNodes[$cursor->name][$elementName])) {
                 // look for inherited child directly
                 if (array_key_exists($cursor->name, $allMappedWrapperXmlNodes) && isset($allMappedWrapperXmlNodes[$cursor->name][$elementName])) {
                     if ($allMappedWrapperXmlNodes[$cursor->name][$elementName] === NULL) {
                         $childClassMetadata = NULL;
                     } else {
                         $childClassMetadata = $this->classMetadataFactory->getMetadataFor($allMappedWrapperXmlNodes[$cursor->name][$elementName]);
                     }
                 } elseif (isset($allMappedXmlNodes[$cursor->name])) {
                     if (is_array($allMappedXmlNodes[$cursor->name])) {
                         // FIXME: This is fragile as it doesn't account for the same xml name being used more than once
                         $childClasses = array_values($allMappedXmlNodes[$cursor->name]);
                         $childClass = array_shift($childClasses);
                     } else {
                         $childClass = $allMappedXmlNodes[$cursor->name];
                     }
                     $childClassMetadata = $this->classMetadataFactory->getMetadataFor($childClass);
                 }
                 // todo: ensure this potential child inherits from parent correctly
                 $fieldName = null;
                 $isWrapper = FALSE;
                 foreach ($classMetadata->getFieldMappings() as $fieldMapping) {
                     if (isset($allMappedXmlNodes[$cursor->name]) && $fieldMapping['type'] == $allMappedXmlNodes[$cursor->name]) {
                         $fieldName = $fieldMapping['fieldName'];
                         break;
                     } elseif (isset($allMappedWrapperXmlNodes[$cursor->name][$elementName]) && $fieldMapping['type'] == $allMappedWrapperXmlNodes[$cursor->name][$elementName]) {
                         $fieldName = $fieldMapping['fieldName'];
                         $isWrapper = TRUE;
                         break;
                     } else {
                         // Walk parent tree
                         if (!isset($childClassMetadata)) {
                             continue;
                         }
                         foreach ($childClassMetadata->getParentClasses() as $parentClass) {
                             if ($fieldMapping['type'] == $parentClass) {
                                 $fieldName = $fieldMapping['fieldName'];
                                 break 2;
                             }
                         }
                     }
                 }
                 if ($fieldName !== null) {
                     $namespace = $cursor->namespaceURI ? $cursor->namespaceURI : $virtualNamespace;
                     if ($isWrapper) {
                         $thisFieldMapping = $classMetadata->getFieldMapping($fieldName);
                         $childEndElement = $thisFieldMapping['wrapper'];
                         $childElements = $this->doUnmarshal($cursor, $childEndElement, $classMetadata, $namespace);
                         $collectionElements[$fieldName] = $childElements;
                     } elseif ($classMetadata->isCollection($fieldName)) {
                         $collectionElements[$fieldName][] = $this->doUnmarshal($cursor, NULL, NULL, $namespace);
                     } else {
                         $classMetadata->setFieldValue($mappedObject, $fieldName, $this->doUnmarshal($cursor, NULL, NULL, $namespace));
                     }
                 }
             } elseif ($this->allowUnknownElements) {
                 $elementName = $cursor->name;
                 $mappedObject->{$elementName} = $this->doUnmarshalUnknownElement($cursor, $endElement, $classMetadata, $virtualNamespace);
             }
         }
         if (!empty($collectionElements)) {
             if (!$isInWrapper || !isset($fieldName)) {
                 foreach ($collectionElements as $fieldName => $elements) {
                     $classMetadata->setFieldValue($mappedObject, $fieldName, $elements);
                 }
             } else {
                 if (!empty($collectionElements[$fieldName]) && (is_array($collectionElements[$fieldName]) && !empty($collectionElements[$fieldName][0]))) {
                     $mappedObject = $collectionElements[$fieldName];
                 }
             }
         }
     }
     if (!$isInWrapper) {
         // PostUnmarshall hook
         if ($classMetadata->hasLifecycleCallbacks(Events::postUnmarshal)) {
             $classMetadata->invokeLifecycleCallbacks(Events::postUnmarshal, $mappedObject);
         }
     }
     return $mappedObject;
 }