/** Get the value from the wrapped POJO, wrapping in DataObjects as necessary. */
 public Object getDeclaredProperty(int propertyIndex) {
   SDOProperty declaredProperty =
       (SDOProperty) dataObject.getType().getDeclaredProperties().get(propertyIndex);
   if (declaredProperty.getType().isChangeSummaryType()) {
     return dataObject.getChangeSummary();
   }
   Mapping mapping = this.getJAXBMappingForProperty(declaredProperty);
   Object value = mapping.getAttributeAccessor().getAttributeValueFromObject(entity);
   if (declaredProperty.isMany()) {
     JAXBListWrapper listWrapper = listWrappers.get(declaredProperty);
     if (null != listWrapper) {
       return listWrapper;
     }
     listWrapper = new JAXBListWrapper(this, declaredProperty);
     listWrappers.put(declaredProperty, listWrapper);
     return listWrapper;
   } else if (null == value || declaredProperty.getType().isDataType()) {
     return value;
   } else {
     if (declaredProperty.isContainment()) {
       return jaxbHelperContext.wrap(value, declaredProperty, dataObject);
     } else {
       return jaxbHelperContext.wrap(value);
     }
   }
 }
 /**
  * For isMany=false properties return true if not null. For collection properties return true if
  * the collection is not empty.
  */
 public boolean isSetDeclaredProperty(int propertyIndex) {
   SDOProperty declaredProperty =
       (SDOProperty) dataObject.getType().getDeclaredProperties().get(propertyIndex);
   if (declaredProperty.getType().isChangeSummaryType()) {
     return true;
   }
   Mapping mapping = this.getJAXBMappingForProperty(declaredProperty);
   if (declaredProperty.isMany()) {
     Collection collection =
         (Collection) mapping.getAttributeAccessor().getAttributeValueFromObject(entity);
     if (null == collection) {
       return false;
     }
     return !collection.isEmpty();
   } else {
     return null != mapping.getAttributeAccessor().getAttributeValueFromObject(entity);
   }
 }
  /** Set the value on the underlying POJO, unwrapping values as necessary. */
  public void setDeclaredProperty(int propertyIndex, Object value) {
    SDOProperty declaredProperty =
        (SDOProperty) dataObject.getType().getDeclaredProperties().get(propertyIndex);
    if (declaredProperty.getType().isChangeSummaryType()) {
      return;
    }

    Mapping mapping = this.getJAXBMappingForProperty(declaredProperty);

    Object newValue = value;
    Object oldValue = mapping.getAttributeAccessor().getAttributeValueFromObject(entity);

    if (declaredProperty.getType().isDataType()) {
      if (!declaredProperty.isMany()) {
        AbstractSession session =
            ((JAXBContext) jaxbHelperContext.getJAXBContext()).getXMLContext().getSession(entity);
        XMLDirectMapping directMapping = (XMLDirectMapping) mapping;
        if (directMapping.hasConverter()) {
          newValue = directMapping.getConverter().convertDataValueToObjectValue(newValue, session);
        } else {
          CoreField field = mapping.getField();
          newValue =
              session
                  .getDatasourcePlatform()
                  .getConversionManager()
                  .convertObject(
                      newValue,
                      descriptor.getObjectBuilder().getFieldClassification((XMLField) field));
        }
      }
      mapping.setAttributeValueInObject(entity, newValue);
    } else if (declaredProperty.isMany()) {
      // Get a ListWrapper and set it's current elements
      ListWrapper listWrapper = (ListWrapper) getDeclaredProperty(propertyIndex);
      listWrapper.addAll((List) newValue);
    } else {
      // OLD VALUE
      if (mapping.isAbstractCompositeObjectMapping()) {
        XMLCompositeObjectMapping compositeMapping = (XMLCompositeObjectMapping) mapping;
        if (oldValue != null && compositeMapping.getContainerAccessor() != null) {
          compositeMapping.getContainerAccessor().setAttributeValueInObject(oldValue, null);
        }
      }

      // NEW VALUE
      newValue = jaxbHelperContext.unwrap((DataObject) value);
      mapping.getAttributeAccessor().setAttributeValueInObject(entity, newValue);
      if (mapping.isAbstractCompositeObjectMapping()) {
        XMLCompositeObjectMapping compositeMapping = (XMLCompositeObjectMapping) mapping;
        if (value != null && compositeMapping.getContainerAccessor() != null) {
          compositeMapping.getContainerAccessor().setAttributeValueInObject(newValue, entity);
        }
      }
    }
  }
  public ValueStore copy() {
    AbstractSession session =
        ((JAXBContext) jaxbHelperContext.getJAXBContext()).getXMLContext().getSession(entity);
    Object originalEntity = entity;
    entity = descriptor.getInstantiationPolicy().buildNewInstance();

    for (SDOProperty sdoProperty : (List<SDOProperty>) dataObject.getType().getProperties()) {
      if (!sdoProperty.getType().isChangeSummaryType()) {
        Mapping mapping = getJAXBMappingForProperty(sdoProperty);
        CoreAttributeAccessor attributeAccessor = mapping.getAttributeAccessor();
        Object attributeValue = attributeAccessor.getAttributeValueFromObject(originalEntity);
        if (mapping.isCollectionMapping()) {
          Object containerCopy = null;
          SDOChangeSummary sdoChangeSummary = dataObject.getChangeSummary();
          if (null != sdoChangeSummary) {
            List list = listWrappers.get(sdoProperty);
            containerCopy = sdoChangeSummary.getOriginalElements().get(list);
          }
          if (null == containerCopy) {
            CoreContainerPolicy containerPolicy = mapping.getContainerPolicy();
            containerCopy = containerPolicy.containerInstance();
            if (null != attributeValue) {
              Object attributeValueIterator = containerPolicy.iteratorFor(attributeValue);
              while (containerPolicy.hasNext(attributeValueIterator)) {
                containerPolicy.addInto(
                    containerPolicy.nextEntry(attributeValueIterator), containerCopy, session);
              }
            }
          }
          attributeAccessor.setAttributeValueInObject(entity, containerCopy);
        } else {
          attributeAccessor.setAttributeValueInObject(entity, attributeValue);
        }
      }
    }
    return new JAXBValueStore(
        jaxbHelperContext, originalEntity, descriptor, dataObject, listWrappers);
  }
  /**
   * For isMany=false properties set the value to null. For isMany=true set the value to an empty
   * container of the appropriate type.
   */
  public void unsetDeclaredProperty(int propertyIndex) {
    SDOProperty declaredProperty =
        (SDOProperty) dataObject.getType().getDeclaredProperties().get(propertyIndex);
    Mapping mapping = this.getJAXBMappingForProperty(declaredProperty);
    if (declaredProperty.isMany()) {
      ContainerMapping containerMapping = (ContainerMapping) mapping;
      ContainerPolicy containerPolicy = containerMapping.getContainerPolicy();

      // OLD VALUE
      if (mapping.isAbstractCompositeCollectionMapping()) {
        XMLCompositeCollectionMapping compositeMapping = (XMLCompositeCollectionMapping) mapping;
        if (compositeMapping.getContainerAccessor() != null) {

          Object oldContainer = mapping.getAttributeValueFromObject(entity);
          if (oldContainer != null) {
            AbstractSession session =
                ((JAXBContext) jaxbHelperContext.getJAXBContext())
                    .getXMLContext()
                    .getSession(entity);
            Object iterator = containerPolicy.iteratorFor(oldContainer);
            while (containerPolicy.hasNext(iterator)) {
              Object oldValue = containerPolicy.next(iterator, session);
              compositeMapping.getContainerAccessor().setAttributeValueInObject(oldValue, null);
            }
          }
        }
      }

      // NEW VALUE
      Object container = containerPolicy.containerInstance();
      mapping.getAttributeAccessor().setAttributeValueInObject(entity, container);
    } else {
      // OLD VALUE
      Object oldValue = mapping.getAttributeAccessor().getAttributeValueFromObject(entity);
      if (mapping.isAbstractCompositeObjectMapping()) {
        XMLCompositeObjectMapping compositeMapping = (XMLCompositeObjectMapping) mapping;
        if (compositeMapping.getContainerAccessor() != null) {
          if (oldValue != null) {
            compositeMapping.getContainerAccessor().setAttributeValueInObject(oldValue, null);
          }
        }
      }

      // NEW VALUE
      mapping.getAttributeAccessor().setAttributeValueInObject(entity, null);
    }
  }
  @Override
  public boolean marshalAttributes(
      MarshalRecord marshalRecord, Object object, CoreAbstractSession session) {
    lazyInitialize();
    boolean hasValue = false;
    NamespaceResolver namespaceResolver = ((Descriptor) descriptor).getNamespaceResolver();

    List<XPathNode> attributeChildren = rootXPathNode.getAttributeChildren();
    if (null != attributeChildren) {
      ObjectMarshalContext objectMarshalContext = ObjectMarshalContext.getInstance();
      for (XPathNode anAttributeChildren : attributeChildren) {
        hasValue =
            anAttributeChildren.marshal(
                    marshalRecord,
                    object,
                    session,
                    namespaceResolver,
                    null,
                    objectMarshalContext,
                    null)
                || hasValue;
      }
    }

    if (rootXPathNode.getAnyAttributeNode() != null) {
      hasValue =
          rootXPathNode
                  .getAnyAttributeNode()
                  .marshal(
                      marshalRecord,
                      object,
                      session,
                      namespaceResolver,
                      null,
                      ObjectMarshalContext.getInstance(),
                      null)
              || hasValue;
    }

    List<XPathNode> selfChildren = rootXPathNode.getSelfChildren();
    if (null != selfChildren) {
      for (XPathNode selfXPathNode : selfChildren) {
        NodeValue marshalNodeValue = selfXPathNode.getMarshalNodeValue();
        if (marshalNodeValue instanceof MappingNodeValue) {
          Mapping selfMapping = ((MappingNodeValue) marshalNodeValue).getMapping();
          Object value = selfMapping.getAttributeValueFromObject(object);
          Descriptor referenceDescriptor = (Descriptor) selfMapping.getReferenceDescriptor();
          Descriptor valueDescriptor;
          if (value != null
              && (referenceDescriptor == null || referenceDescriptor.hasInheritance())) {
            valueDescriptor = (Descriptor) session.getDescriptor(value.getClass());
          } else {
            valueDescriptor = referenceDescriptor;
          }
          if (null != valueDescriptor) {
            marshalRecord.addXsiTypeAndClassIndicatorIfRequired(
                valueDescriptor, referenceDescriptor, (Field) selfMapping.getField(), false);
          }
        }
        selfXPathNode.marshalSelfAttributes(
            marshalRecord, object, session, namespaceResolver, marshalRecord.getMarshaller());
      }
    }

    return hasValue;
  }
  void lazyInitialize() {
    if (initialized) {
      return;
    }
    synchronized (this) {
      if (initialized) {
        return;
      }
      Descriptor xmlDescriptor = (Descriptor) descriptor;

      // MAPPINGS
      Iterator mappingIterator = xmlDescriptor.getMappings().iterator();
      Iterator fieldTransformerIterator;
      Mapping xmlMapping;

      // Transformation Mapping
      TransformationMapping transformationMapping;
      FieldTransformerNodeValue fieldTransformerNodeValue;
      Object[] nextFieldToTransformer;

      // Simple Type Translator
      TypeNodeValue typeNodeValue;

      NodeValue mappingNodeValue = null;
      Field xmlField;
      while (mappingIterator.hasNext()) {
        xmlMapping = (Mapping) mappingIterator.next();

        xmlField = (Field) xmlMapping.getField();
        if (xmlMapping.isTransformationMapping()) {
          transformationMapping = (TransformationMapping) xmlMapping;
          addTransformationMapping(transformationMapping);
          fieldTransformerIterator = transformationMapping.getFieldToTransformers().iterator();
          while (fieldTransformerIterator.hasNext()) {
            fieldTransformerNodeValue = new FieldTransformerNodeValue(transformationMapping);
            nextFieldToTransformer = (Object[]) fieldTransformerIterator.next();
            xmlField = (Field) nextFieldToTransformer[0];
            fieldTransformerNodeValue.setXMLField(xmlField);
            fieldTransformerNodeValue.setFieldTransformer(
                (CoreFieldTransformer) nextFieldToTransformer[1]);
            addChild(
                xmlField.getXPathFragment(),
                fieldTransformerNodeValue,
                xmlDescriptor.getNamespaceResolver());
          }
        } else {
          if (xmlMapping.isAbstractDirectMapping()) {
            mappingNodeValue = new XMLDirectMappingNodeValue((DirectMapping) xmlMapping);
          } else if (xmlMapping.isAbstractCompositeObjectMapping()) {
            mappingNodeValue =
                new XMLCompositeObjectMappingNodeValue((CompositeObjectMapping) xmlMapping);
          } else if (xmlMapping.isAbstractCompositeCollectionMapping()) {
            CompositeCollectionMapping collectionMapping = (CompositeCollectionMapping) xmlMapping;
            mappingNodeValue = new XMLCompositeCollectionMappingNodeValue(collectionMapping);
            if (collectionMapping.getWrapperNullPolicy() != null) {
              addChild(
                  xmlField.getXPathFragment(),
                  new CollectionGroupingElementNodeValue((ContainerValue) mappingNodeValue),
                  xmlDescriptor.getNamespaceResolver());
            }
          } else if (xmlMapping.isAbstractCompositeDirectCollectionMapping()) {
            DirectCollectionMapping collectionMapping = (DirectCollectionMapping) xmlMapping;
            mappingNodeValue = new XMLCompositeDirectCollectionMappingNodeValue(collectionMapping);
            if (collectionMapping.getWrapperNullPolicy() != null) {
              addChild(
                  xmlField.getXPathFragment(),
                  new CollectionGroupingElementNodeValue((ContainerValue) mappingNodeValue),
                  xmlDescriptor.getNamespaceResolver());
            }
          } else if (xmlMapping instanceof InverseReferenceMapping) {
            xmlMapping = (Mapping) ((InverseReferenceMapping) xmlMapping).getInlineMapping();
            if (xmlMapping == null) {
              continue;
            }
            xmlField = (Field) xmlMapping.getField();
            if (xmlMapping.isAbstractCompositeCollectionMapping()) {
              mappingNodeValue =
                  new XMLCompositeCollectionMappingNodeValue(
                      (CompositeCollectionMapping) xmlMapping, true);
            }
            if (xmlMapping.isAbstractCompositeObjectMapping()) {
              mappingNodeValue =
                  new XMLCompositeObjectMappingNodeValue((CompositeObjectMapping) xmlMapping, true);
            }

          } else if (xmlMapping instanceof VariableXPathCollectionMapping) {
            mappingNodeValue =
                new XMLVariableXPathCollectionMappingNodeValue(
                    (VariableXPathCollectionMapping) xmlMapping);
          } else if (xmlMapping instanceof VariableXPathObjectMapping) {
            mappingNodeValue =
                new XMLVariableXPathObjectMappingNodeValue((VariableXPathObjectMapping) xmlMapping);
          } else if (xmlMapping instanceof AnyObjectMapping) {
            mappingNodeValue = new XMLAnyObjectMappingNodeValue((AnyObjectMapping) xmlMapping);
          } else if (xmlMapping instanceof AnyCollectionMapping) {
            mappingNodeValue =
                new XMLAnyCollectionMappingNodeValue((AnyCollectionMapping) xmlMapping);
          } else if (xmlMapping instanceof AnyAttributeMapping) {
            mappingNodeValue =
                new XMLAnyAttributeMappingNodeValue((AnyAttributeMapping) xmlMapping);
          } else if (xmlMapping instanceof BinaryDataMapping) {
            mappingNodeValue = new XMLBinaryDataMappingNodeValue((BinaryDataMapping) xmlMapping);
          } else if (xmlMapping instanceof BinaryDataCollectionMapping) {
            mappingNodeValue =
                new XMLBinaryDataCollectionMappingNodeValue(
                    (BinaryDataCollectionMapping) xmlMapping);
          } else if (xmlMapping instanceof FragmentMapping) {
            mappingNodeValue = new XMLFragmentMappingNodeValue((FragmentMapping) xmlMapping);
          } else if (xmlMapping instanceof FragmentCollectionMapping) {
            mappingNodeValue =
                new XMLFragmentCollectionMappingNodeValue((FragmentCollectionMapping) xmlMapping);
          } else if (xmlMapping instanceof CollectionReferenceMapping) {
            CollectionReferenceMapping xmlColMapping = (CollectionReferenceMapping) xmlMapping;

            List fields = xmlColMapping.getFields();
            Field xmlColMappingField = (Field) xmlColMapping.getField();
            XPathNode branchNode;
            if (null == xmlColMappingField) {
              if (fields.size() > 1 && !xmlColMapping.usesSingleNode()) {
                addChild(
                    XPathFragment.SELF_FRAGMENT,
                    new XMLCollectionReferenceMappingMarshalNodeValue(xmlColMapping),
                    xmlDescriptor.getNamespaceResolver());
              }
              branchNode = rootXPathNode;
            } else {
              branchNode =
                  addChild(
                      ((Field) xmlColMapping.getField()).getXPathFragment(),
                      new XMLCollectionReferenceMappingMarshalNodeValue(xmlColMapping),
                      xmlDescriptor.getNamespaceResolver());
            }

            int containerIndex = -1;
            for (int i = 0, size = fields.size(); i < size; i++) {
              Field xmlFld = (Field) fields.get(i);
              mappingNodeValue = new XMLCollectionReferenceMappingNodeValue(xmlColMapping, xmlFld);
              if (i == 0) {
                addContainerValue((ContainerValue) mappingNodeValue);
                containerIndex = ((ContainerValue) mappingNodeValue).getIndex();
              } else {
                ((ContainerValue) mappingNodeValue).setIndex(containerIndex);
              }
              branchNode.addChild(
                  xmlFld.getXPathFragment(),
                  mappingNodeValue,
                  xmlDescriptor.getNamespaceResolver());
            }
            continue;
          } else if (xmlMapping instanceof ObjectReferenceMapping) {
            ObjectReferenceMapping xmlORMapping = (ObjectReferenceMapping) xmlMapping;
            Iterator fieldIt = xmlORMapping.getFields().iterator();
            while (fieldIt.hasNext()) {
              Field xmlFld = (Field) fieldIt.next();
              mappingNodeValue = new XMLObjectReferenceMappingNodeValue(xmlORMapping, xmlFld);
              addChild(
                  xmlFld.getXPathFragment(),
                  mappingNodeValue,
                  xmlDescriptor.getNamespaceResolver());
            }
            continue;
          } else if (xmlMapping instanceof ChoiceObjectMapping) {
            ChoiceObjectMapping xmlChoiceMapping = (ChoiceObjectMapping) xmlMapping;
            Iterator fields = xmlChoiceMapping.getChoiceElementMappings().keySet().iterator();
            Field firstField = (Field) fields.next();
            XMLChoiceObjectMappingNodeValue firstNodeValue =
                new XMLChoiceObjectMappingNodeValue(xmlChoiceMapping, firstField);
            firstNodeValue.setNullCapableNodeValue(firstNodeValue);
            addChild(
                firstField.getXPathFragment(),
                firstNodeValue,
                xmlDescriptor.getNamespaceResolver());
            while (fields.hasNext()) {
              Field next = (Field) fields.next();
              XMLChoiceObjectMappingNodeValue nodeValue =
                  new XMLChoiceObjectMappingNodeValue(xmlChoiceMapping, next);
              nodeValue.setNullCapableNodeValue(firstNodeValue);
              addChild(next.getXPathFragment(), nodeValue, xmlDescriptor.getNamespaceResolver());
            }
            continue;
          } else if (xmlMapping instanceof ChoiceCollectionMapping) {
            ChoiceCollectionMapping xmlChoiceMapping = (ChoiceCollectionMapping) xmlMapping;

            Iterator<Entry<Field, Mapping>> fields =
                xmlChoiceMapping.getChoiceElementMappings().entrySet().iterator();
            Entry<Field, Mapping> firstEntry = fields.next();
            Field firstField = firstEntry.getKey();

            XMLChoiceCollectionMappingUnmarshalNodeValue unmarshalValue =
                new XMLChoiceCollectionMappingUnmarshalNodeValue(xmlChoiceMapping, firstField);
            XMLChoiceCollectionMappingMarshalNodeValue marshalValue =
                new XMLChoiceCollectionMappingMarshalNodeValue(xmlChoiceMapping, firstField);

            // The reason behind LinkedHashMap is the order of items when for-cycling
            // HashMap.getEntrySet() or HashMap.getKeySet().
            // This change fixes non-determinism (implementation in JDK8 has changed so the order is
            // different (sometimes) than in JDK6 and JDK7).
            HashMap<Field, NodeValue> fieldToNodeValues = new LinkedHashMap<Field, NodeValue>();
            unmarshalValue.setContainerNodeValue(unmarshalValue);
            unmarshalValue.setFieldToNodeValues(fieldToNodeValues);
            if (xmlChoiceMapping.isMixedContent()
                && (xmlChoiceMapping.getMixedContentMapping() == firstEntry.getValue())) {
              unmarshalValue.setIsMixedNodeValue(true);
              marshalValue.setIsMixedNodeValue(true);
            }
            this.addContainerValue(unmarshalValue);
            ((ContainerValue) unmarshalValue.getChoiceElementNodeValue())
                .setIndex(unmarshalValue.getIndex());
            fieldToNodeValues.put(firstField, unmarshalValue);
            addChild(
                firstField.getXPathFragment(),
                unmarshalValue,
                xmlDescriptor.getNamespaceResolver());
            addChild(
                firstField.getXPathFragment(), marshalValue, xmlDescriptor.getNamespaceResolver());
            while (fields.hasNext()) {
              Entry<Field, Mapping> nextEntry = fields.next();
              Field nextField = nextEntry.getKey();
              XMLChoiceCollectionMappingUnmarshalNodeValue nodeValue =
                  new XMLChoiceCollectionMappingUnmarshalNodeValue(xmlChoiceMapping, nextField);
              nodeValue.setContainerNodeValue(unmarshalValue);
              nodeValue.setIndex(unmarshalValue.getIndex());
              ((ContainerValue) nodeValue.getChoiceElementNodeValue())
                  .setIndex(unmarshalValue.getIndex());
              addChild(
                  nextField.getXPathFragment(), nodeValue, xmlDescriptor.getNamespaceResolver());
              fieldToNodeValues.put(nextField, nodeValue);
              if (xmlChoiceMapping.isMixedContent()
                  && (xmlChoiceMapping.getMixedContentMapping() == nextEntry.getValue())) {
                nodeValue.setIsMixedNodeValue(true);
              }
            }
            if (xmlChoiceMapping.isAny()) {
              XMLChoiceCollectionMappingUnmarshalNodeValue nodeValue =
                  new XMLChoiceCollectionMappingUnmarshalNodeValue(
                      xmlChoiceMapping, null, xmlChoiceMapping.getAnyMapping());
              nodeValue.setContainerNodeValue(unmarshalValue);
              nodeValue.setIndex(unmarshalValue.getIndex());
              ((ContainerValue) nodeValue.getChoiceElementNodeValue())
                  .setIndex(unmarshalValue.getIndex());
              addChild(null, nodeValue, xmlDescriptor.getNamespaceResolver());
              fieldToNodeValues.put(null, nodeValue);
              if (xmlChoiceMapping.isMixedContent()) {
                nodeValue.setIsMixedNodeValue(true);
              }
            }
            marshalValue.setFieldToNodeValues(fieldToNodeValues);
            continue;
          }
          if (mappingNodeValue.isContainerValue()) {
            addContainerValue((ContainerValue) mappingNodeValue);
          }
          if (mappingNodeValue.isNullCapableValue()) {
            addNullCapableValue((NullCapableValue) mappingNodeValue);
          }
          if (xmlField != null) {
            addChild(
                xmlField.getXPathFragment(),
                mappingNodeValue,
                xmlDescriptor.getNamespaceResolver());
          } else {
            addChild(null, mappingNodeValue, xmlDescriptor.getNamespaceResolver());
          }
        }
      }

      if (descriptor.hasInheritance()) {
        Field indicatorField = (Field) descriptor.getInheritancePolicy().getClassIndicatorField();
        if (indicatorField != null) {
          if (indicatorField.getLastXPathFragment().getNamespaceURI() != null
              && indicatorField
                  .getLastXPathFragment()
                  .getNamespaceURI()
                  .equals(javax.xml.XMLConstants.W3C_XML_SCHEMA_INSTANCE_NS_URI)
              && indicatorField
                  .getLastXPathFragment()
                  .getLocalName()
                  .equals(Constants.SCHEMA_TYPE_ATTRIBUTE)) {
            xsiTypeIndicatorField = true;
          }
        }
      }

      initialized = true;
    }
  }