/** 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); } } } }
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; }
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); } } } }
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()); } } }
/** * 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; }
/** * 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; }
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())); } } }