String findField(final MappedClass mc, final IndexOptions options, final List<String> path) { String segment = path.get(0); if (segment.equals("$**")) { return segment; } MappedField mf = mc.getMappedField(segment); if (mf == null) { mf = mc.getMappedFieldByJavaField(segment); } if (mf == null && mc.isInterface()) { for (final MappedClass mappedClass : mapper.getSubTypes(mc)) { try { return findField(mappedClass, options, new ArrayList<String>(path)); } catch (MappingException e) { // try the next one } } } String namePath; if (mf != null) { namePath = mf.getNameToStore(); } else { if (!options.disableValidation()) { throw pathFail(mc, path); } else { return join(path, '.'); } } if (path.size() > 1) { try { Class concreteType = !mf.isSingleValue() ? mf.getSubClass() : mf.getConcreteType(); namePath += "." + findField( mapper.getMappedClass(concreteType), options, path.subList(1, path.size())); } catch (MappingException e) { if (!options.disableValidation()) { throw pathFail(mc, path); } else { return join(path, '.'); } } } return namePath; }
/** Validate the path, and value type, returning the mapped field for the field at the path */ static MappedField validateQuery( final Class clazz, final Mapper mapper, final StringBuilder origProp, final FilterOperator op, final Object val, final boolean validateNames, final boolean validateTypes) { // TODO: cache validations (in static?). MappedField mf = null; final String prop = origProp.toString(); boolean hasTranslations = false; if (validateNames) { final String[] parts = prop.split("\\."); if (clazz == null) { return null; } MappedClass mc = mapper.getMappedClass(clazz); // CHECKSTYLE:OFF for (int i = 0; ; ) { // CHECKSTYLE:ON final String part = parts[i]; boolean fieldIsArrayOperator = part.equals("$"); mf = mc.getMappedField(part); // translate from java field name to stored field name if (mf == null && !fieldIsArrayOperator) { mf = mc.getMappedFieldByJavaField(part); if (mf == null) { throw new ValidationException( format( "The field '%s' could not be found in '%s' while validating - %s; if " + "you wish to continue please disable validation.", part, clazz.getName(), prop)); } hasTranslations = true; parts[i] = mf.getNameToStore(); } i++; if (mf != null && mf.isMap()) { // skip the map key validation, and move to the next part i++; } if (i >= parts.length) { break; } if (!fieldIsArrayOperator) { // catch people trying to search/update into @Reference/@Serialized fields if (!canQueryPast(mf)) { throw new ValidationException( format( "Can not use dot-notation past '%s' could not be found in '%s' while" + " validating - %s", part, clazz.getName(), prop)); } // get the next MappedClass for the next field validation mc = mapper.getMappedClass((mf.isSingleValue()) ? mf.getType() : mf.getSubClass()); } } // record new property string if there has been a translation to any part if (hasTranslations) { origProp.setLength(0); // clear existing content origProp.append(parts[0]); for (int i = 1; i < parts.length; i++) { origProp.append('.'); origProp.append(parts[i]); } } if (validateTypes && mf != null) { List<ValidationFailure> typeValidationFailures = new ArrayList<ValidationFailure>(); boolean compatibleForType = isCompatibleForOperator(mc, mf, mf.getType(), op, val, typeValidationFailures); List<ValidationFailure> subclassValidationFailures = new ArrayList<ValidationFailure>(); boolean compatibleForSubclass = isCompatibleForOperator(mc, mf, mf.getSubClass(), op, val, subclassValidationFailures); if ((mf.isSingleValue() && !compatibleForType) || mf.isMultipleValues() && !(compatibleForSubclass || compatibleForType)) { if (LOG.isWarningEnabled()) { LOG.warning( format( "The type(s) for the query/update may be inconsistent; using an instance of type '%s' " + "for the field '%s.%s' which is declared as '%s'", val.getClass().getName(), mf.getDeclaringClass().getName(), mf.getJavaFieldName(), mf.getType().getName())); typeValidationFailures.addAll(subclassValidationFailures); LOG.warning("Validation warnings: \n" + typeValidationFailures); } } } } return mf; }