private List<Index> collectTopLevelIndexes(final MappedClass mc) { List<Index> list = new ArrayList<Index>(); final List<Indexes> annotations = mc.getAnnotations(Indexes.class); if (annotations != null) { for (final Indexes indexes : annotations) { for (final Index index : indexes.value()) { Index updated = index; if (index.fields().length == 0) { LOG.warning( format( "This index on '%s' is using deprecated configuration options. Please update to use the " + "fields value on @Index: %s", mc.getClazz().getName(), index.toString())); updated = new IndexBuilder().migrate(index); } List<Field> fields = new ArrayList<Field>(); for (Field field : updated.fields()) { fields.add( new FieldBuilder() .value(findField(mc, index.options(), asList(field.value().split("\\.")))) .type(field.type()) .weight(field.weight())); } list.add(replaceFields(updated, fields)); } } } return list; }
@Override public Query<T> retrieveKnownFields() { final MappedClass mc = ds.getMapper().getMappedClass(clazz); final List<String> fields = new ArrayList<String>(mc.getPersistenceFields().size() + 1); for (final MappedField mf : mc.getPersistenceFields()) { fields.add(mf.getNameToStore()); } retrievedFields(true, fields.toArray(new String[fields.size()])); return this; }
@Override public void check(final MappedClass mc, final Set<ConstraintViolation> ve) { if (mc.getEmbeddedAnnotation() != null && mc.getIdField() != null) { ve.add( new ConstraintViolation( Level.FATAL, mc, getClass(), "@" + Embedded.class.getSimpleName() + " classes cannot specify a @Id field")); } }
@Override protected void check( final MappedClass mc, final MappedField mf, final Set<ConstraintViolation> ve) { if (mf.hasAnnotation(Reference.class)) { final Class realType = (mf.isSingleValue()) ? mf.getType() : mf.getSubClass(); if (realType == null) { throw new MappingException("Type is null for this MappedField: " + mf); } if ((!realType.isInterface() && mc.getMapper().getMappedClass(realType).getIdField() == null)) { ve.add( new ConstraintViolation( Level.FATAL, mc, mf, getClass(), mf.getFullName() + " is annotated as a @" + Reference.class.getSimpleName() + " but the " + mf.getType().getName() + " class is missing the @" + Id.class.getSimpleName() + " annotation")); } } }
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; }
/** * Creates a Query for the given type and collection * * @param clazz the type to return * @param coll the collection to query * @param ds the Datastore to use */ public QueryImpl(final Class<T> clazz, final DBCollection coll, final Datastore ds) { super(CriteriaJoin.AND); setQuery(this); this.clazz = clazz; this.ds = ((org.mongodb.morphia.DatastoreImpl) ds); dbColl = coll; cache = this.ds.getMapper().createEntityCache(); final MappedClass mc = this.ds.getMapper().getMappedClass(clazz); final Entity entAn = mc == null ? null : mc.getEntityAnnotation(); if (entAn != null) { getOptions() .readPreference( this.ds.getMapper().getMappedClass(clazz).getEntityAnnotation().queryNonPrimary() ? ReadPreference.secondaryPreferred() : null); } }
@Override @Deprecated public DBObject getFieldsObject() { DBObject projection = getOptions().getProjection(); if (projection == null || projection.keySet().size() == 0) { return null; } final MappedClass mc = ds.getMapper().getMappedClass(clazz); Entity entityAnnotation = mc.getEntityAnnotation(); final BasicDBObject fieldsFilter = copy(projection); if (includeFields && entityAnnotation != null && !entityAnnotation.noClassnameStored()) { fieldsFilter.put(Mapper.CLASS_NAME_FIELDNAME, 1); } return fieldsFilter; }
@Override public void check(final MappedClass mc, final Set<ConstraintViolation> ve) { final Set<Class<?>> classesToInspect = new HashSet<Class<?>>(); for (final Field field : ReflectionUtils.getDeclaredAndInheritedFields(mc.getClazz(), true)) { if (isFieldToInspect(field) && !field.isAnnotationPresent(Id.class)) { classesToInspect.add(field.getType()); } } checkRecursivelyHasNoIdAnnotationPresent(classesToInspect, new HashSet<Class<?>>(), mc, ve); }
private List<Index> collectIndexes(final MappedClass mc, final List<MappedClass> parentMCs) { if (parentMCs.contains(mc) || mc.getEmbeddedAnnotation() != null && parentMCs.isEmpty()) { return emptyList(); } List<Index> indexes = collectTopLevelIndexes(mc); indexes.addAll(collectFieldIndexes(mc)); indexes.addAll(collectNestedIndexes(mc, parentMCs)); return indexes; }
@SuppressWarnings("deprecation") private List<Index> collectFieldIndexes(final MappedClass mc) { List<Index> list = new ArrayList<Index>(); for (final MappedField mf : mc.getPersistenceFields()) { if (mf.hasAnnotation(Indexed.class)) { final Indexed indexed = mf.getAnnotation(Indexed.class); list.add(convert(indexed, mf.getNameToStore())); } else if (mf.hasAnnotation(Text.class)) { final Text text = mf.getAnnotation(Text.class); list.add(convert(text, mf.getNameToStore())); } } return list; }
private List<Index> collectNestedIndexes( final MappedClass mc, final List<MappedClass> parentMCs) { List<Index> list = new ArrayList<Index>(); for (final MappedField mf : mc.getPersistenceFields()) { if (!mf.isTypeMongoCompatible() && !mf.hasAnnotation(Reference.class) && !mf.hasAnnotation(Serialized.class) && !mf.hasAnnotation(NotSaved.class) && !mf.isTransient()) { final List<MappedClass> parents = new ArrayList<MappedClass>(parentMCs); parents.add(mc); List<MappedClass> classes = new ArrayList<MappedClass>(); MappedClass mappedClass = mapper.getMappedClass(mf.isSingleValue() ? mf.getType() : mf.getSubClass()); classes.add(mappedClass); classes.addAll(mapper.getSubTypes(mappedClass)); for (MappedClass aClass : classes) { for (Index index : collectIndexes(aClass, parents)) { List<Field> fields = new ArrayList<Field>(); for (Field field : index.fields()) { fields.add( new FieldBuilder() .value( field.value().equals("$**") ? field.value() : mf.getNameToStore() + "." + field.value()) .type(field.type()) .weight(field.weight())); } list.add(new IndexBuilder(index).fields(fields)); } } } } return list; }
BsonDocument calculateKeys(final MappedClass mc, final Index index) { BsonDocument keys = new BsonDocument(); for (Field field : index.fields()) { String path; try { path = findField( mc, index.options(), new ArrayList<String>(asList(field.value().split("\\.")))); } catch (Exception e) { path = field.value(); String message = format( "The path '%s' can not be validated against '%s' and may represent an invalid index", path, mc.getClazz().getName()); if (!index.options().disableValidation()) { throw new MappingException(message); } LOG.warning(message); } keys.putAll(toBsonDocument(path, field.type().toIndexValue())); } return keys; }
/** 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; }
private MappingException pathFail(final MappedClass mc, final List<String> path) { return new MappingException( format( "Could not resolve path '%s' against '%s'.", join(path, '.'), mc.getClazz().getName())); }