/**
  * Return the JAXB mapping for the SDO property. They are matched on their XML schema
  * representation.
  */
 Mapping getJAXBMappingForProperty(SDOProperty sdoProperty) {
   DatabaseMapping sdoMapping = sdoProperty.getXmlMapping();
   XMLField field;
   if (sdoMapping instanceof XMLObjectReferenceMapping) {
     XMLObjectReferenceMapping referenceMapping = (XMLObjectReferenceMapping) sdoMapping;
     field = (XMLField) referenceMapping.getFields().get(0);
   } else {
     field = (XMLField) sdoMapping.getField();
   }
   TreeObjectBuilder treeObjectBuilder = (TreeObjectBuilder) descriptor.getObjectBuilder();
   XPathNode xPathNode = treeObjectBuilder.getRootXPathNode();
   XPathFragment xPathFragment = field.getXPathFragment();
   while (xPathNode != null && xPathFragment != null) {
     if (xPathFragment.isAttribute()) {
       if (sdoProperty.isMany()
           && !sdoProperty.isContainment()
           && !sdoProperty.getType().isDataType()) {
         xPathFragment = null;
         break;
       }
       Map attributeChildrenMap = xPathNode.getAttributeChildrenMap();
       if (null == attributeChildrenMap) {
         xPathNode = null;
       } else {
         xPathNode = (XPathNode) attributeChildrenMap.get(xPathFragment);
       }
     } else if (xPathFragment.nameIsText()) {
       xPathNode = xPathNode.getTextNode();
     } else {
       Map nonAttributeChildrenMap = xPathNode.getNonAttributeChildrenMap();
       if (null == nonAttributeChildrenMap) {
         xPathNode = null;
       } else {
         xPathNode = (XPathNode) nonAttributeChildrenMap.get(xPathFragment);
       }
     }
     xPathFragment = xPathFragment.getNextFragment();
     if (xPathFragment != null && xPathFragment.nameIsText()) {
       if (sdoProperty.isMany() && !sdoProperty.isContainment()) {
         xPathFragment = null;
         break;
       }
     }
   }
   if (null == xPathFragment && xPathNode != null) {
     if (xPathNode.getNodeValue().isMappingNodeValue()) {
       MappingNodeValue mappingNodeValue = (MappingNodeValue) xPathNode.getNodeValue();
       return mappingNodeValue.getMapping();
     }
   }
   throw SDOException.sdoJaxbNoMappingForProperty(sdoProperty.getName(), field.getXPath());
 }
 /** 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);
     }
   }
 }
  /**
   * Returns true if the property is declared as an element in the XSD. Returns false if not known
   * or for advanced cases. It is possible for both isAttribute and isElement to return false but
   * they will not both return true.
   *
   * @param property to identify if an element.
   * @return true if the property is declared as an element in the XSD.
   */
  public boolean isElement(Property property) {
    if (property == null) {
      return false;
    }
    SDOProperty sdoProperty = (SDOProperty) property;
    Object value = sdoProperty.get(SDOConstants.XMLELEMENT_PROPERTY);
    if ((value != null) && value instanceof Boolean) {
      return ((Boolean) value).booleanValue();
    }

    if ((sdoProperty.getOpposite() != null) && (sdoProperty.getOpposite().isContainment())) {
      return false;
    } else if (sdoProperty.isMany() || sdoProperty.isContainment() || sdoProperty.isNullable()) {
      return true;
    }

    return false;
  }
  /**
   * INTERNAL: Build the copy tree and cache all reachable DataObjects with their copy<br>
   * Cache all non-containment properties - to be set after tree construction<br>
   * Recurse the tree in preorder traversal (root, child1-n) Scope: We do not have to check the
   * copyTree scope when iterating opposites since we will not enter any opposite property dataTree
   * that is outside of the copyTree scope
   *
   * @param doMap (cache original -> copy DataObject instances to set non-containment properties
   *     after tree construction)
   * @param ncPropMap (cache original DO:non-containment property values to be set after tree
   *     construction)
   */
  private SDODataObject copyPrivate(
      SDODataObject dataObject,
      HashMap doMap, //
      HashMap ncPropMap,
      SDOChangeSummary cs)
      throws IllegalArgumentException {
    // check for null DataObject
    if (null == dataObject) {
      return null; // this is acceptable behavior
    }

    SDODataObject copy =
        (SDODataObject)
            getHelperContext() //
                .getDataFactory()
                .create(dataObject.getType().getURI(), dataObject.getType().getName());

    // store current object for reference by the non-containment map
    doMap.put(dataObject, copy);

    List ocListOriginal = dataObject._getOpenContentProperties();
    for (Iterator anOCIterator = ocListOriginal.iterator(); anOCIterator.hasNext(); ) {
      copy.addOpenContentProperty((Property) anOCIterator.next());
    }

    List ocAttrsListOriginal = dataObject._getOpenContentPropertiesAttributes();
    for (Iterator anOCAttrIterator = ocAttrsListOriginal.iterator(); anOCAttrIterator.hasNext(); ) {
      copy.addOpenContentProperty((Property) anOCAttrIterator.next());
    }

    //  start iterating all copy's properties
    for (Iterator iterInstanceProperties = copy.getInstanceProperties().iterator();
        iterInstanceProperties.hasNext(); ) {
      SDOProperty eachProperty = (SDOProperty) iterInstanceProperties.next();
      boolean isSet = isSet(dataObject, eachProperty, cs);
      if (isSet) {
        Object o = getValue(dataObject, eachProperty, cs);
        if (eachProperty.getType().isDataType()) {
          if (!eachProperty.getType().isChangeSummaryType()) {

            /**
             * ChangeSummaries must be cleared with logging set to the original state without
             * creating oldSettings. The logging flag from the original will be set on the copy
             * after this copy call completes and gets set on its container. The cs is off by
             * default in the copy object. updateSequence flag is false - we will populate the
             * sequence in order after subtree creation
             */
            copy.setInternal(eachProperty, o, false);
          }
        } else {
          // case matrix for containment and opposite combinations
          // cont=false, opp=false -> unidirectional
          // cont=false, opp=true  -> bidirectional
          // cont=true,  opp=false -> normal containment
          // cont=true,  opp=true  -> bidirectional
          if (eachProperty.isContainment()) {
            // process containment properties (normal, half of bidirectionals)
            copyContainmentPropertyValue(copy, eachProperty, o, doMap, ncPropMap, cs);
          } else {
            // copy non-containment do (not properties (unidirectional, half of bidirectionals))
            // copyPropertyValue(dataObject, copy, eachProperty, o, copyRoot, doMap);
            // cacheNContainmentPropertyValue(copy, eachProperty, o, doMap, ncPropMap);
            // store non-containment properties
            ArrayList anArray = (ArrayList) ncPropMap.get(dataObject);
            if (anArray == null) {
              anArray = new ArrayList();
              anArray.add(eachProperty);
              // store property array into the map for the first time
              ncPropMap.put(dataObject, anArray);
            } else {
              // add to existing array of nc properties in the map
              anArray.add(eachProperty);
            }
          }
        }
      }
    }

    // sequences will not be processed until the entire tree is copied so we can resolve any
    // reference relationships
    return copy;
  }