Example #1
0
 /** Copies the array with an appropriate class depending on the type. */
 protected static Object[] typedArray(Type type, Object[] array) {
   if (array == null) {
     array = EMPTY_STRING_ARRAY;
   }
   Class<?> klass;
   if (type instanceof StringType) {
     klass = String.class;
   } else if (type instanceof BooleanType) {
     klass = Boolean.class;
   } else if (type instanceof LongType) {
     klass = Long.class;
   } else if (type instanceof DoubleType) {
     klass = Double.class;
   } else if (type instanceof DateType) {
     klass = Calendar.class;
   } else if (type instanceof BinaryType) {
     klass = String.class;
   } else if (type instanceof IntegerType) {
     throw new RuntimeException("Unimplemented primitive type: " + type.getClass().getName());
   } else if (type instanceof SimpleTypeImpl) {
     // simple type with constraints -- ignore constraints XXX
     return typedArray(type.getSuperType(), array);
   } else {
     throw new RuntimeException("Invalid primitive type: " + type.getClass().getName());
   }
   int len = array.length;
   Object[] copy = (Object[]) Array.newInstance(klass, len);
   System.arraycopy(array, 0, copy, 0, len);
   return copy;
 }
Example #2
0
 protected Field getField(Field parent, String subFieldName, boolean finalCall) {
   if (parent != null) {
     Type type = parent.getType();
     if (type.isListType()) {
       ListType listType = (ListType) type;
       // remove indexes in case of multiple values
       if ("*".equals(subFieldName)) {
         if (!finalCall) {
           return parent;
         } else {
           return resolveSubField(listType, null, true);
         }
       }
       try {
         Integer.valueOf(subFieldName);
         if (!finalCall) {
           return parent;
         } else {
           return resolveSubField(listType, null, true);
         }
       } catch (NumberFormatException e) {
         return resolveSubField(listType, subFieldName, false);
       }
     } else if (type.isComplexType()) {
       return ((ComplexType) type).getField(subFieldName);
     }
   }
   return null;
 }
Example #3
0
 protected void visitBlobsField(T state, Field field) throws PropertyException {
   Type type = field.getType();
   if (type.isSimpleType()) {
     // scalar
   } else if (type.isComplexType()) {
     // complex property
     String name = field.getName().getPrefixedName();
     T childState = getChild(state, name, type);
     if (childState != null) {
       path.addLast(name);
       visitBlobsComplex(childState, (ComplexType) type);
       path.removeLast();
     }
   } else {
     // array or list
     Type fieldType = ((ListType) type).getFieldType();
     if (fieldType.isSimpleType()) {
       // array
     } else {
       // complex list
       String name = field.getName().getPrefixedName();
       path.addLast(name);
       int i = 0;
       for (T childState : getChildAsList(state, name)) {
         path.addLast(String.valueOf(i++));
         visitBlobsComplex(childState, (ComplexType) fieldType);
         path.removeLast();
       }
       path.removeLast();
     }
   }
 }
Example #4
0
 protected Object getValueField(T state, Field field) throws PropertyException {
   Type type = field.getType();
   String name = field.getName().getPrefixedName();
   name = internalName(name);
   if (type.isSimpleType()) {
     // scalar
     return state.getSingle(name);
   } else if (type.isComplexType()) {
     // complex property
     T childState = getChild(state, name, type);
     if (childState == null) {
       return null;
     }
     return getValueComplex(childState, (ComplexType) type);
   } else {
     // array or list
     Type fieldType = ((ListType) type).getFieldType();
     if (fieldType.isSimpleType()) {
       // array
       return state.getArray(name);
     } else {
       // complex list
       List<T> childStates = getChildAsList(state, name);
       List<Object> list = new ArrayList<>(childStates.size());
       for (T childState : childStates) {
         Object value = getValueComplex(childState, (ComplexType) fieldType);
         list.add(value);
       }
       return list;
     }
   }
 }
Example #5
0
 protected Field resolveSubField(ListType listType, String subName, boolean fallbackOnSubElement) {
   Type itemType = listType.getFieldType();
   if (itemType.isComplexType() && subName != null) {
     ComplexType complexType = (ComplexType) itemType;
     Field subField = complexType.getField(subName);
     return subField;
   }
   if (fallbackOnSubElement) {
     return listType.getField();
   }
   return null;
 }
Example #6
0
 /** Reads state into a complex property. */
 protected void readComplexProperty(T state, ComplexProperty complexProperty)
     throws PropertyException {
   if (state == null) {
     complexProperty.init(null);
     return;
   }
   if (complexProperty instanceof BlobProperty) {
     Blob blob = getValueBlob(state);
     complexProperty.init((Serializable) blob);
     return;
   }
   for (Property property : complexProperty) {
     String name = property.getField().getName().getPrefixedName();
     name = internalName(name);
     Type type = property.getType();
     if (type.isSimpleType()) {
       // simple property
       Object value = state.getSingle(name);
       if (value instanceof Delta) {
         value = ((Delta) value).getFullValue();
       }
       property.init((Serializable) value);
     } else if (type.isComplexType()) {
       // complex property
       T childState = getChild(state, name, type);
       readComplexProperty(childState, (ComplexProperty) property);
       ((ComplexProperty) property).removePhantomFlag();
     } else {
       ListType listType = (ListType) type;
       if (listType.getFieldType().isSimpleType()) {
         // array
         Object[] array = state.getArray(name);
         array = typedArray(listType.getFieldType(), array);
         property.init(array);
       } else {
         // complex list
         Field listField = listType.getField();
         List<T> childStates = getChildAsList(state, name);
         // TODO property.init(null) if null children in DBS
         List<Object> list = new ArrayList<>(childStates.size());
         for (T childState : childStates) {
           ComplexProperty p =
               (ComplexProperty) complexProperty.getRoot().createProperty(property, listField, 0);
           readComplexProperty(childState, p);
           list.add(p.getValue());
         }
         property.init((Serializable) list);
       }
     }
   }
 }
Example #7
0
 /**
  * Builds the property.
  *
  * @param document the document
  * @param parent the parent
  * @param field the field
  * @param dateVal the dateVal
  * @throws IOException Signals that an I/O exception has occurred.
  */
 private static void buildProperty(Document document, Element parent, Field field, Object value)
     throws IOException {
   Type type = field.getType();
   // no need to qualify each element name as namespace is already added
   String propName = field.getName().getLocalName();
   Element element = document.createElement(propName);
   parent.appendChild(element);
   // extract the element content
   if (type.isSimpleType()) {
     // Avoid returning scientific notation representations of
     // very large or very small decimal values. See CSPACE-4691.
     if (isNuxeoDecimalType(type) && valueMatchesNuxeoType(type, value)) {
       element.setTextContent(nuxeoDecimalValueToDecimalString(value));
       /*
       * We need a way to produce just a Date when the specified data
       * type is an xs:date vs. xs:datetime. Nuxeo maps both to a Calendar. Sigh.
       if(logger.isTraceEnabled() && isDateType(type)) {
           String dateValType = "unknown";
           if (value instanceof java.util.Date) {
               dateValType = "java.util.Date";
           } else if (value instanceof java.util.Calendar) {
               dateValType = "java.util.Calendar";
           }
           logger.trace("building XML for date type: "+type.getName()
                   +" value type: "+dateValType
                   +" encoded: "+encodedVal);
       }
       */
     } else {
       String encodedVal = type.encode(value);
       element.setTextContent(encodedVal);
     }
   } else if (type.isComplexType()) {
     ComplexType ctype = (ComplexType) type;
     if (ctype.getName().equals(TypeConstants.CONTENT)) {
       throw new RuntimeException("Unexpected schema type: BLOB for field: " + propName);
     } else {
       buildComplex(document, element, ctype, (Map) value);
     }
   } else if (type.isListType()) {
     if (value instanceof List) {
       buildList(document, element, (ListType) type, (List) value);
     } else if (value.getClass().getComponentType() != null) {
       buildList(document, element, (ListType) type, PrimitiveArrays.toList(value));
     } else {
       throw new IllegalArgumentException(
           "A value of list type is neither list neither array: " + value);
     }
   }
 }
Example #8
0
 protected void readPrefetchField(
     T state,
     Field field,
     String xpathGeneric,
     String xpath,
     Set<String> prefixes,
     Map<String, Serializable> prefetch) {
   String name = field.getName().getPrefixedName();
   Type type = field.getType();
   xpathGeneric = xpathGeneric == null ? name : xpathGeneric + '/' + name;
   xpath = xpath == null ? name : xpath + '/' + name;
   if (!prefixes.contains(xpathGeneric)) {
     return;
   }
   if (type.isSimpleType()) {
     // scalar
     Object value = state.getSingle(name);
     prefetch.put(xpath, (Serializable) value);
   } else if (type.isComplexType()) {
     // complex property
     T childState = getChild(state, name, type);
     if (childState != null) {
       readPrefetch(childState, (ComplexType) type, xpathGeneric, xpath, prefixes, prefetch);
     }
   } else {
     // array or list
     ListType listType = (ListType) type;
     if (listType.getFieldType().isSimpleType()) {
       // array
       Object[] value = state.getArray(name);
       prefetch.put(xpath, value);
     } else {
       // complex list
       List<T> childStates = getChildAsList(state, name);
       Field listField = listType.getField();
       xpathGeneric += "/*";
       int i = 0;
       for (T childState : childStates) {
         readPrefetch(
             childState,
             (ComplexType) listField.getType(),
             xpathGeneric,
             xpath + '/' + i++,
             prefixes,
             prefetch);
       }
     }
   }
 }
Example #9
0
 private static boolean valueMatchesNuxeoType(Type type, Object value) {
   try {
     return type.validate(value);
   } catch (TypeException te) {
     return false;
   }
 }
Example #10
0
  protected DocumentType recomputeDocumentType(
      String name, Set<String> stack, Map<String, DocumentTypeDescriptor> dtds) {
    DocumentTypeImpl docType = documentTypes.get(name);
    if (docType != null) {
      // already done
      return docType;
    }
    if (stack.contains(name)) {
      log.error("Document type: " + name + " used in parent inheritance loop: " + stack);
      return null;
    }
    DocumentTypeDescriptor dtd = dtds.get(name);
    if (dtd == null) {
      log.error("Document type: " + name + " does not exist, used as parent by type: " + stack);
      return null;
    }

    // find and recompute the parent first
    DocumentType parent;
    String parentName = dtd.superTypeName;
    if (parentName == null) {
      parent = null;
    } else {
      parent = documentTypes.get(parentName);
      if (parent == null) {
        stack.add(name);
        parent = recomputeDocumentType(parentName, stack, dtds);
        stack.remove(name);
      }
    }

    // what it extends
    for (Type p = parent; p != null; p = p.getSuperType()) {
      Set<String> set = documentTypesExtending.get(p.getName());
      set.add(name);
    }

    return recomputeDocumentType(name, dtd, parent);
  }
Example #11
0
 protected void setValueField(T state, Field field, Object value) throws PropertyException {
   Type type = field.getType();
   String name = field.getName().getPrefixedName(); // normalize from map key
   name = internalName(name);
   // TODO we could check for read-only here
   if (type.isSimpleType()) {
     // scalar
     state.setSingle(name, value);
   } else if (type.isComplexType()) {
     // complex property
     T childState = getChildForWrite(state, name, type);
     setValueComplex(childState, field, value);
   } else {
     // array or list
     ListType listType = (ListType) type;
     Type fieldType = listType.getFieldType();
     if (fieldType.isSimpleType()) {
       // array
       if (value instanceof List) {
         value = ((List<?>) value).toArray(new Object[0]);
       }
       state.setArray(name, (Object[]) value);
     } else {
       // complex list
       if (value != null && !(value instanceof List)) {
         throw new PropertyException(
             "Expected List value for: "
                 + name
                 + ", got "
                 + value.getClass().getName()
                 + " instead");
       }
       @SuppressWarnings("unchecked")
       List<Object> values = value == null ? Collections.emptyList() : (List<Object>) value;
       updateList(state, name, values, listType.getField());
     }
   }
 }
 protected void writeListProperty(JsonGenerator jg, Property prop) throws IOException {
   jg.writeStartArray();
   if (prop instanceof ArrayProperty) {
     Object[] ar = (Object[]) prop.getValue();
     if (ar == null) {
       jg.writeEndArray();
       return;
     }
     Type itemType = ((ListType) prop.getType()).getFieldType();
     ObjectResolver resolver = itemType.getObjectResolver();
     String path = prop.getPath();
     for (Object o : ar) {
       if (!fetchProperty(jg, resolver, o, path)) {
         writeScalarPropertyValue(jg, ((SimpleType) itemType).getPrimitiveType(), o);
       }
     }
   } else {
     ListProperty listp = (ListProperty) prop;
     for (Property p : listp.getChildren()) {
       writeProperty(jg, p);
     }
   }
   jg.writeEndArray();
 }
 private void writeScalarPropertyValue(JsonGenerator jg, Type type, Object value)
     throws IOException {
   if (value == null) {
     jg.writeNull();
   } else if (type instanceof BooleanType) {
     jg.writeBoolean((Boolean) value);
   } else if (type instanceof LongType) {
     jg.writeNumber((Long) value);
   } else if (type instanceof DoubleType) {
     jg.writeNumber((Double) value);
   } else if (type instanceof IntegerType) {
     jg.writeNumber((Integer) value);
   } else if (type instanceof BinaryType) {
     jg.writeBinary((byte[]) value);
   } else {
     jg.writeString(type.encode(value));
   }
 }
Example #14
0
 protected void registerType(Type type) {
   types.put(type.getName(), type);
 }
Example #15
0
  /**
   * Gets the element data.
   *
   * @param element the element
   * @param type the type
   * @return the element data
   */
  @SuppressWarnings("unchecked")
  private static Object getElementData(org.dom4j.Element element, Type type, ServiceContext ctx)
      throws Exception {
    Object result = null;
    String dateStr = "";

    if (type.isSimpleType()) {
      if (isNuxeoDateType(type)) {
        String dateVal = element.getText();
        if (dateVal == null || dateVal.trim().isEmpty()) {
          result = type.decode("");
        } else {
          // Dates or date/times in any ISO 8601-based representations
          // directly supported by Nuxeo will be successfully decoded.
          result = type.decode(dateVal);
          // All other date or date/time values must first be converted
          // to a supported ISO 8601-based representation.
          if (result == null) {
            dateStr = DateTimeFormatUtils.toIso8601Timestamp(dateVal, ctx.getTenantId());
            if (dateStr != null) {
              result = type.decode(dateStr);
            } else {
              throw new IllegalArgumentException(
                  "Unrecognized date value '" + dateVal + "' in field '" + element.getName() + "'");
            }
          }
        }
      } else {
        String textValue = element.getText();
        if (textValue != null && textValue.trim().isEmpty()) {
          result = null;
        } else {
          result = type.decode(textValue);
        }
      }
    } else if (type.isListType()) {
      ListType ltype = (ListType) type;
      List<Object> list = new ArrayList<Object>();
      Iterator<org.dom4j.Element> it = element.elementIterator();
      while (it.hasNext()) {
        org.dom4j.Element el = it.next();
        list.add(getElementData(el, ltype.getFieldType(), ctx));
      }
      Type ftype = ltype.getFieldType();
      if (ftype.isSimpleType()) { // these are stored as arrays
        Class klass = JavaTypes.getClass(ftype);
        if (klass.isPrimitive()) {
          return PrimitiveArrays.toPrimitiveArray(list, klass);
        } else {
          return list.toArray((Object[]) Array.newInstance(klass, list.size()));
        }
      }
      result = list;
    } else {
      ComplexType ctype = (ComplexType) type;
      if (ctype.getName().equals(TypeConstants.CONTENT)) {
        //				String mimeType = element.elementText(ExportConstants.BLOB_MIME_TYPE);
        //				String encoding = element.elementText(ExportConstants.BLOB_ENCODING);
        //				String content = element.elementTextTrim(ExportConstants.BLOB_DATA);
        //				if ((content == null || content.length() == 0)
        //						&& (mimeType == null || mimeType.length() == 0)) {
        //					return null; // remove blob
        //				}
        //				Blob blob = null;
        //				if (xdoc.hasExternalBlobs()) {
        //					blob = xdoc.getBlob(content);
        //				}
        //				if (blob == null) { // may be the blob is embedded like a Base64
        //					// encoded data
        //					byte[] bytes = Base64.decode(content);
        //					blob = new StreamingBlob(new ByteArraySource(bytes));
        //				}
        //				blob.setMimeType(mimeType);
        //				blob.setEncoding(encoding);
        //				return blob;
      } else { // a complex type
        Map<String, Object> map = new HashMap<String, Object>();
        Iterator<org.dom4j.Element> it = element.elementIterator();
        while (it.hasNext()) {
          org.dom4j.Element el = it.next();
          String name = el.getName();
          Object value = getElementData(el, ctype.getField(el.getName()).getType(), ctx);
          map.put(name, value);
        }
        result = map;
      }
    }
    return result;
  }
Example #16
0
  /**
   * Writes state from a complex property.
   *
   * <p>Writes only properties that are dirty.
   *
   * @return {@code true} if something changed
   */
  protected boolean writeComplexProperty(
      T state, ComplexProperty complexProperty, String xpath, WriteContext wc)
      throws PropertyException {
    @SuppressWarnings("unchecked")
    BlobWriteContext<T> writeContext = (BlobWriteContext<T>) wc;
    if (complexProperty instanceof BlobProperty) {
      Serializable value = ((BlobProperty) complexProperty).getValueForWrite();
      if (value != null && !(value instanceof Blob)) {
        throw new PropertyException("Cannot write a non-Blob value: " + value);
      }
      writeContext.recordBlob(state, (Blob) value, this);
      return true;
    }
    boolean changed = false;
    for (Property property : complexProperty) {
      // write dirty properties, but also phantoms with non-null default values
      // this is critical for DeltaLong updates to work, they need a non-null initial value
      if (property.isDirty()
          || (property.isPhantom() && property.getField().getDefaultValue() != null)) {
        // do the write
      } else {
        continue;
      }
      String name = property.getField().getName().getPrefixedName();
      name = internalName(name);
      if (checkReadOnlyIgnoredWrite(property, state)) {
        continue;
      }
      String xp = xpath == null ? name : xpath + '/' + name;
      writeContext.recordChange(xp);
      changed = true;

      Type type = property.getType();
      if (type.isSimpleType()) {
        // simple property
        Serializable value = property.getValueForWrite();
        state.setSingle(name, value);
        if (value instanceof Delta) {
          value = ((Delta) value).getFullValue();
          ((ScalarProperty) property).internalSetValue(value);
        }
      } else if (type.isComplexType()) {
        // complex property
        T childState = getChildForWrite(state, name, type);
        writeComplexProperty(childState, (ComplexProperty) property, xp, writeContext);
      } else {
        ListType listType = (ListType) type;
        if (listType.getFieldType().isSimpleType()) {
          // array
          Serializable value = property.getValueForWrite();
          if (value instanceof List) {
            List<?> list = (List<?>) value;
            Object[] array;
            if (list.isEmpty()) {
              array = new Object[0];
            } else {
              // use properly-typed array, useful for mem backend that doesn't re-convert all types
              Class<?> klass = list.get(0).getClass();
              array = (Object[]) Array.newInstance(klass, list.size());
            }
            value = list.toArray(array);
          } else if (value instanceof Object[]) {
            Object[] ar = (Object[]) value;
            if (ar.length != 0) {
              // use properly-typed array, useful for mem backend that doesn't re-convert all types
              Class<?> klass = Object.class;
              for (Object o : ar) {
                if (o != null) {
                  klass = o.getClass();
                  break;
                }
              }
              Object[] array;
              if (ar.getClass().getComponentType() == klass) {
                array = ar;
              } else {
                // copy to array with proper component type
                array = (Object[]) Array.newInstance(klass, ar.length);
                System.arraycopy(ar, 0, array, 0, ar.length);
              }
              value = array;
            }
          } else if (value == null) {
            // ok
          } else {
            throw new IllegalStateException(value.toString());
          }
          state.setArray(name, (Object[]) value);
        } else {
          // complex list
          // update it
          List<T> childStates = updateList(state, name, property);
          // write values
          int i = 0;
          for (Property childProperty : property.getChildren()) {
            T childState = childStates.get(i);
            String xpi = xp + '/' + i;
            boolean c =
                writeComplexProperty(
                    childState, (ComplexProperty) childProperty, xpi, writeContext);
            if (c) {
              writeContext.recordChange(xpi);
            }
            i++;
          }
        }
      }
    }
    return changed;
  }
Example #17
0
  /** Gets a value (may be complex/list) from the document at the given xpath. */
  protected Object getValueObject(T state, String xpath) throws PropertyException {
    xpath = canonicalXPath(xpath);
    String[] segments = xpath.split("/");

    /*
     * During this loop state may become null if we read an uninitialized complex property (DBS), in that case the
     * code must treat it as reading uninitialized values for its children.
     */
    ComplexType parentType = getType();
    for (int i = 0; i < segments.length; i++) {
      String segment = segments[i];
      Field field = parentType.getField(segment);
      if (field == null && i == 0) {
        // check facets
        SchemaManager schemaManager = Framework.getService(SchemaManager.class);
        for (String facet : getFacets()) {
          CompositeType facetType = schemaManager.getFacet(facet);
          field = facetType.getField(segment);
          if (field != null) {
            break;
          }
        }
      }
      if (field == null && i == 0 && getProxySchemas() != null) {
        // check proxy schemas
        for (Schema schema : getProxySchemas()) {
          field = schema.getField(segment);
          if (field != null) {
            break;
          }
        }
      }
      if (field == null) {
        throw new PropertyNotFoundException(xpath, i == 0 ? null : "Unknown segment: " + segment);
      }
      String name = field.getName().getPrefixedName(); // normalize from segment
      Type type = field.getType();

      // check if we have a complex list index in the next position
      if (i < segments.length - 1 && StringUtils.isNumeric(segments[i + 1])) {
        int index = Integer.parseInt(segments[i + 1]);
        i++;
        if (!type.isListType() || ((ListType) type).getFieldType().isSimpleType()) {
          throw new PropertyNotFoundException(xpath, "Cannot use index after segment: " + segment);
        }
        List<T> list = state == null ? Collections.emptyList() : getChildAsList(state, name);
        if (index >= list.size()) {
          throw new PropertyNotFoundException(xpath, "Index out of bounds: " + index);
        }
        // find complex list state
        state = list.get(index);
        parentType = (ComplexType) ((ListType) type).getFieldType();
        if (i == segments.length - 1) {
          // last segment
          return getValueComplex(state, parentType);
        } else {
          // not last segment
          continue;
        }
      }

      if (i == segments.length - 1) {
        // last segment
        return state == null ? null : getValueField(state, field);
      } else {
        // not last segment
        if (type.isSimpleType()) {
          // scalar
          throw new PropertyNotFoundException(xpath, "Segment must be last: " + segment);
        } else if (type.isComplexType()) {
          // complex property
          state = state == null ? null : getChild(state, name, type);
          // here state can be null (DBS), continue loop with it, meaning uninitialized for read
          parentType = (ComplexType) type;
        } else {
          // list
          ListType listType = (ListType) type;
          if (listType.isArray()) {
            // array of scalars
            throw new PropertyNotFoundException(xpath, "Segment must be last: " + segment);
          } else {
            // complex list but next segment was not numeric
            throw new PropertyNotFoundException(
                xpath, "Missing list index after segment: " + segment);
          }
        }
      }
    }
    throw new AssertionError("not reached");
  }
Example #18
0
  /** Sets a value (may be complex/list) into the document at the given xpath. */
  protected void setValueObject(T state, String xpath, Object value) throws PropertyException {
    xpath = canonicalXPath(xpath);
    String[] segments = xpath.split("/");

    ComplexType parentType = getType();
    for (int i = 0; i < segments.length; i++) {
      String segment = segments[i];
      Field field = parentType.getField(segment);
      if (field == null && i == 0) {
        // check facets
        SchemaManager schemaManager = Framework.getService(SchemaManager.class);
        for (String facet : getFacets()) {
          CompositeType facetType = schemaManager.getFacet(facet);
          field = facetType.getField(segment);
          if (field != null) {
            break;
          }
        }
      }
      if (field == null && i == 0 && getProxySchemas() != null) {
        // check proxy schemas
        for (Schema schema : getProxySchemas()) {
          field = schema.getField(segment);
          if (field != null) {
            break;
          }
        }
      }
      if (field == null) {
        throw new PropertyNotFoundException(xpath, i == 0 ? null : "Unknown segment: " + segment);
      }
      String name = field.getName().getPrefixedName(); // normalize from segment
      Type type = field.getType();

      // check if we have a complex list index in the next position
      if (i < segments.length - 1 && StringUtils.isNumeric(segments[i + 1])) {
        int index = Integer.parseInt(segments[i + 1]);
        i++;
        if (!type.isListType() || ((ListType) type).getFieldType().isSimpleType()) {
          throw new PropertyNotFoundException(xpath, "Cannot use index after segment: " + segment);
        }
        List<T> list = getChildAsList(state, name);
        if (index >= list.size()) {
          throw new PropertyNotFoundException(xpath, "Index out of bounds: " + index);
        }
        // find complex list state
        state = list.get(index);
        field = ((ListType) type).getField();
        if (i == segments.length - 1) {
          // last segment
          setValueComplex(state, field, value);
        } else {
          // not last segment
          parentType = (ComplexType) field.getType();
        }
        continue;
      }

      if (i == segments.length - 1) {
        // last segment
        setValueField(state, field, value);
      } else {
        // not last segment
        if (type.isSimpleType()) {
          // scalar
          throw new PropertyNotFoundException(xpath, "Segment must be last: " + segment);
        } else if (type.isComplexType()) {
          // complex property
          state = getChildForWrite(state, name, type);
          parentType = (ComplexType) type;
        } else {
          // list
          ListType listType = (ListType) type;
          if (listType.isArray()) {
            // array of scalars
            throw new PropertyNotFoundException(xpath, "Segment must be last: " + segment);
          } else {
            // complex list but next segment was not numeric
            throw new PropertyNotFoundException(
                xpath, "Missing list index after segment: " + segment);
          }
        }
      }
    }
  }