/** * Load schema. * * @param schema the schema * @param schemaElement the schema element * @return the map * @throws Exception the exception */ @SuppressWarnings("unchecked") private static Map<String, Object> loadSchema( Schema schema, org.dom4j.Element schemaElement, ServiceContext ctx) throws Exception { String schemaName1 = schemaElement.attributeValue( ExportConstants.NAME_ATTR); // FIXME: Do we need this local var? String schemaName = schema.getName(); Map<String, Object> data = new HashMap<String, Object>(); Iterator<org.dom4j.Element> it = schemaElement.elementIterator(); while (it.hasNext()) { org.dom4j.Element element = it.next(); String name = element.getName(); Field field = schema.getField(name); if (field != null) { Object value = getElementData(element, field.getType(), ctx); data.put(name, value); } else { // FIXME: substitute an appropriate constant for "csid" below. // One potential class to which to add that constant, if it is not already // declared, might be AbstractCollectionSpaceResourceImpl - ADR 2012-09-24 if (!name.equals("csid")) { // 'csid' elements in input payloads can be safely ignored. logger.warn( "Invalid input document. No such property was found [" + name + "] in schema " + schemaName); } } } return data; }
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; } } }
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(); } } }
@Override public String toString() { if (listItem) { return field.getName().getPrefixedName(); } else { return field.getName().getPrefixedName() + "[" + index + "]"; } }
protected Object getValueComplex(T state, ComplexType complexType) throws PropertyException { if (TypeConstants.isContentType(complexType)) { return getValueBlob(state); } Map<String, Object> map = new HashMap<>(); for (Field field : complexType.getFields()) { String name = field.getName().getPrefixedName(); Object value = getValueField(state, field); map.put(name, value); } return map; }
/** * 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); } } }
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 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; }
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()); } } }
// pass field instead of just type for better error messages protected void setValueComplex(T state, Field field, Object value) throws PropertyException { ComplexType complexType = (ComplexType) field.getType(); if (TypeConstants.isContentType(complexType)) { if (value != null && !(value instanceof Blob)) { throw new PropertyException( "Expected Blob value for: " + field.getName().getPrefixedName() + ", got " + value.getClass().getName() + " instead"); } setValueBlob(state, (Blob) value); return; } if (value != null && !(value instanceof Map)) { throw new PropertyException( "Expected Map value for: " + field.getName().getPrefixedName() + ", got " + value.getClass().getName() + " instead"); } @SuppressWarnings("unchecked") Map<String, Object> map = value == null ? Collections.emptyMap() : (Map<String, Object>) value; Set<String> keys = new HashSet<>(map.keySet()); for (Field f : complexType.getFields()) { String name = f.getName().getPrefixedName(); keys.remove(name); value = map.get(name); setValueField(state, f, value); } if (!keys.isEmpty()) { throw new PropertyException( "Unknown key: " + keys.iterator().next() + " for " + field.getName().getPrefixedName()); } }
/** Recomputes all the info needed for efficient access. */ private void recomputeSourceInfos() throws DirectoryException { final Schema schema = schemaManager.getSchema(schemaName); if (schema == null) { throw new DirectoryException( String.format("Directory '%s' has unknown schema '%s'", directory.getName(), schemaName)); } final Set<String> sourceFields = new HashSet<String>(); for (Field f : schema.getFields()) { sourceFields.add(f.getName().getLocalName()); } if (!sourceFields.contains(schemaIdField)) { throw new DirectoryException( String.format( "Directory '%s' schema '%s' has no id field '%s'", directory.getName(), schemaName, schemaIdField)); } List<SourceInfo> newSourceInfos = new ArrayList<SourceInfo>(2); for (SourceDescriptor source : descriptor.sources) { int ndirs = source.subDirectories.length; if (ndirs == 0) { throw new DirectoryException( String.format( "Directory '%s' source '%s' has no subdirectories", directory.getName(), source.name)); } final List<SubDirectoryInfo> subDirectoryInfos = new ArrayList<SubDirectoryInfo>(ndirs); SubDirectoryInfo authDirectoryInfo = null; boolean hasRequiredDir = false; for (SubDirectoryDescriptor subDir : source.subDirectories) { final String dirName = subDir.name; final String dirSchemaName = directoryService.getDirectorySchema(dirName); final String dirIdField = directoryService.getDirectoryIdField(dirName); final boolean dirIsAuth = directoryService.getDirectoryPasswordField(dirName) != null; final Map<String, String> fromSource = new HashMap<String, String>(); final Map<String, String> toSource = new HashMap<String, String>(); final Map<String, Serializable> defaultEntry = new HashMap<String, Serializable>(); final boolean dirIsOptional = subDir.isOptional; // XXX check authenticating final Schema dirSchema = schemaManager.getSchema(dirSchemaName); if (dirSchema == null) { throw new DirectoryException( String.format( "Directory '%s' source '%s' subdirectory '%s' " + "has unknown schema '%s'", directory.getName(), source.name, dirName, dirSchemaName)); } // record default field mappings if same name and record default // values final Set<String> dirSchemaFields = new HashSet<String>(); for (Field f : dirSchema.getFields()) { final String fieldName = f.getName().getLocalName(); dirSchemaFields.add(fieldName); if (sourceFields.contains(fieldName)) { // XXX check no duplicates! fromSource.put(fieldName, fieldName); toSource.put(fieldName, fieldName); } // XXX cast to Serializable defaultEntry.put(fieldName, (Serializable) f.getDefaultValue()); } // treat renamings // XXX id field ? for (FieldDescriptor field : subDir.fields) { final String sourceFieldName = field.forField; final String fieldName = field.name; if (!sourceFields.contains(sourceFieldName)) { throw new DirectoryException( String.format( "Directory '%s' source '%s' subdirectory '%s' " + "has mapping for unknown field '%s'", directory.getName(), source.name, dirName, sourceFieldName)); } if (!dirSchemaFields.contains(fieldName)) { throw new DirectoryException( String.format( "Directory '%s' source '%s' subdirectory '%s' " + "has mapping of unknown field' '%s'", directory.getName(), source.name, dirName, fieldName)); } fromSource.put(sourceFieldName, fieldName); toSource.put(fieldName, sourceFieldName); } SubDirectoryInfo subDirectoryInfo = new SubDirectoryInfo( dirName, dirSchemaName, dirIdField, dirIsAuth, fromSource, toSource, defaultEntry, dirIsOptional); subDirectoryInfos.add(subDirectoryInfo); if (dirIsAuth) { if (authDirectoryInfo != null) { throw new DirectoryException( String.format( "Directory '%s' source '%s' has two subdirectories " + "with a password field, '%s' and '%s'", directory.getName(), source.name, authDirectoryInfo.dirName, dirName)); } authDirectoryInfo = subDirectoryInfo; } if (!dirIsOptional) { hasRequiredDir = true; } } if (isAuthenticating() && authDirectoryInfo == null) { throw new DirectoryException( String.format( "Directory '%s' source '%s' has no subdirectory " + "with a password field", directory.getName(), source.name)); } if (!hasRequiredDir) { throw new DirectoryException( String.format( "Directory '%s' source '%s' only has optional subdirectories: " + "no directory can be used has a reference.", directory.getName(), source.name)); } newSourceInfos.add(new SourceInfo(source, subDirectoryInfos, authDirectoryInfo)); } sourceInfos = newSourceInfos; }
@Override public Type getType() { return field.getType(); }
@Override public String getName() { return field.getName().getPrefixedName(); }
/** 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); } } } } }
/** 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"); }