protected Object resolveComplex(Element el, AttributeConfigDescriptor conf) {
    Map<String, Object> propValue = new HashMap<>();
    for (String name : conf.getMapping().keySet()) {
      propValue.put(name, resolveAndEvaluateXmlNode(el, conf.getMapping().get(name)));
    }

    return propValue;
  }
 protected List<AttributeConfigDescriptor> getAttributConfigs(Element el) {
   List<AttributeConfigDescriptor> result = new ArrayList<>();
   for (AttributeConfigDescriptor conf : getRegistry().getAttributConfigs()) {
     if (conf.getTagName().equals(el.getName())) {
       result.add(conf);
     } else {
       // try xpath match
       try {
         if (el.matches(conf.getTagName())) {
           result.add(conf);
         }
       } catch (InvalidXPathException e) {
         // NOP
       }
     }
   }
   return result;
 }
  protected void createNewDocument(Element el, DocConfigDescriptor conf) {
    DocumentModel doc = session.createDocumentModel(conf.getDocType());

    String path = resolvePath(el, conf.getParent());
    Object nameOb = resolveName(el, conf.getName());
    String name = null;
    if (nameOb == null) {
      if (log.isDebugEnabled()) {
        log.debug(String.format(MSG_NO_ELEMENT_FOUND, conf.getName(), el.getUniquePath()));
      }
      int idx = 1;
      for (int i = 0; i < docsStack.size(); i++) {
        if (docsStack.get(i).getType().equals(conf.getDocType())) {
          idx++;
        }
      }
      name = conf.getDocType() + "-" + idx;
    } else {
      name = nameOb.toString();
    }
    doc.setPathInfo(path, name);

    if (log.isDebugEnabled()) {
      if (conf.getUpdate()) {
        log.debug(String.format(MSG_UPDATE, path, name, el.getUniquePath(), conf.toString()));
      } else {
        log.debug(String.format(MSG_CREATION, path, name, el.getUniquePath(), conf.toString()));
      }
    }

    try {
      if (conf.getUpdate() && session.exists(doc.getRef())) {
        DocumentModel existingDoc = session.getDocument(doc.getRef());

        // get attributes, if attribute needs to be overwritten, empty in the document
        for (Object e : el.elements()) {
          List<AttributeConfigDescriptor> configs = getAttributConfigs((Element) e);
          if (configs != null) {
            if (!deletedAttributes.containsKey(existingDoc.getId())) {
              deletedAttributes.put(existingDoc.getId(), new ArrayList<String>());
            }
            for (AttributeConfigDescriptor config : configs) {
              String targetDocProperty = config.getTargetDocProperty();
              // check deletedAttributes for attribute which should be overwritten
              // if it is there, don't empty it a second time
              if (config.overwrite
                  && !deletedAttributes.get(existingDoc.getId()).contains(targetDocProperty)) {
                deletedAttributes.get(existingDoc.getId()).add(targetDocProperty);
                existingDoc.setPropertyValue(targetDocProperty, new ArrayList<>());
              }
            }
          }
        }
        doc = existingDoc;
      } else {
        doc = session.createDocument(doc);
      }
    } catch (NuxeoException e) {
      e.addInfo(String.format(MSG_CREATION, path, name, el.getUniquePath(), conf.toString()));
      throw e;
    }
    pushInStack(doc);
    elToDoc.put(el, doc);
  }
  protected void processDocAttributes(
      DocumentModel doc, Element el, AttributeConfigDescriptor conf) {
    String targetDocProperty = conf.getTargetDocProperty();

    if (log.isDebugEnabled()) {
      log.debug(
          String.format(
              MSG_UPDATE_PROPERTY,
              targetDocProperty,
              el.getUniquePath(),
              doc.getPathAsString(),
              doc.getType(),
              conf.toString()));
    }
    Property property = doc.getProperty(targetDocProperty);

    if (property.isScalar()) {
      Object value = resolveAndEvaluateXmlNode(el, conf.getSingleXpath());
      if (log.isTraceEnabled()) {
        log.trace(
            String.format(
                MSG_UPDATE_PROPERTY_TRACE,
                targetDocProperty,
                el.getUniquePath(),
                value,
                conf.toString()));
      }
      property.setValue(value);

    } else if (property.isComplex()) {

      if (property instanceof BlobProperty) {
        Object value = resolveBlob(el, conf);
        if (log.isTraceEnabled()) {
          log.trace(
              String.format(
                  MSG_UPDATE_PROPERTY_TRACE,
                  targetDocProperty,
                  el.getUniquePath(),
                  value,
                  conf.toString()));
        }
        property.setValue(value);
      } else {
        Object value = resolveComplex(el, conf);
        if (log.isTraceEnabled()) {
          log.trace(
              String.format(
                  MSG_UPDATE_PROPERTY_TRACE,
                  targetDocProperty,
                  el.getUniquePath(),
                  value,
                  conf.toString()));
        }
        property.setValue(value);
      }

    } else if (property.isList()) {

      ListType lType = (ListType) property.getType();

      Serializable value;

      if (lType.getFieldType().isSimpleType()) {
        value = (Serializable) resolveAndEvaluateXmlNode(el, conf.getSingleXpath());
        if (value != null) {
          Object values = property.getValue();
          if (values instanceof List) {
            ((List) values).add(value);
            property.setValue(values);
          } else if (values instanceof Object[]) {
            List<Object> valuesList = new ArrayList<>();
            Collections.addAll(valuesList, (Object[]) property.getValue());
            valuesList.add(value);
            property.setValue(valuesList.toArray());
          } else {
            log.error(
                "Simple multi value property "
                    + targetDocProperty
                    + " is neither a List nor an Array");
          }
        }
      } else {
        value = (Serializable) resolveComplex(el, conf);
        if (value != null && !conf.getMapping().isEmpty()) {
          property.addValue(value);
        }
      }

      if (log.isTraceEnabled()) {
        log.trace(
            String.format(
                MSG_UPDATE_PROPERTY_TRACE,
                targetDocProperty,
                el.getUniquePath(),
                value,
                conf.toString()));
      }
    }
  }