/*
 * Copyright 2016 MongoDB, Inc.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *   http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package org.mongodb.morphia.internal;

import org.mongodb.morphia.mapping.MappedClass;
import org.mongodb.morphia.mapping.MappedField;
import org.mongodb.morphia.mapping.Mapper;
import org.mongodb.morphia.query.ValidationException;

import java.util.Iterator;
import java.util.List;

import static java.lang.String.format;
import static java.util.Arrays.asList;
import static org.mongodb.morphia.internal.MorphiaUtils.join;

This is an internal class and is subject to change or removal.
Since:1.3
/** * This is an internal class and is subject to change or removal. * * @since 1.3 */
public class PathTarget { private final String path; private final List<String> segments; private boolean validateNames = true; private int position; private Mapper mapper; private MappedClass context; private MappedClass root; private MappedField target; private boolean resolved = false;
Creates a resolution context for the given root and path.
Params:
  • mapper – mapper
  • root – root
  • path – path
/** * Creates a resolution context for the given root and path. * * @param mapper mapper * @param root root * @param path path */
public PathTarget(final Mapper mapper, final MappedClass root, final String path) { this.root = root; segments = asList(path.split("\\.")); this.mapper = mapper; this.path = path; }
Disables validation of path segments.
/** * Disables validation of path segments. */
public void disableValidation() { resolved = false; validateNames = false; } private boolean hasNext() { return position < segments.size(); }
Returns the translated path for this context. If validation is disabled, that path could be the same as the initial value.
Returns:the translated path
/** * Returns the translated path for this context. If validation is disabled, that path could be the same as the initial value. * * @return the translated path */
public String translatedPath() { if (!resolved) { resolve(); } return join(segments, '.'); }
Returns the MappedField found at the end of a path. May be null if the path is invalid and validation is disabled.
Returns:the field
/** * Returns the MappedField found at the end of a path. May be null if the path is invalid and validation is disabled. * * @return the field */
public MappedField getTarget() { if (!resolved) { resolve(); } return target; } String next() { return segments.get(position++); } private void resolve() { context = this.root; position = 0; MappedField field = null; while (hasNext()) { String segment = next(); if (segment.equals("$") || segment.matches("[0-9]+")) { // array operator if (!hasNext()) { return; } segment = next(); } field = resolveField(segment); if (field != null) { translate(field.getNameToStore()); if (field.isMap() && hasNext()) { next(); // consume the map key segment } } else { if (validateNames) { throw new ValidationException(format("Could not resolve path '%s' against '%s'.", join(segments, '.'), root.getClazz().getName())); } } } target = field; resolved = true; } private void translate(final String nameToStore) { segments.set(position - 1, nameToStore); } private MappedField resolveField(final String segment) { MappedField mf = context.getMappedField(segment); if (mf == null) { mf = context.getMappedFieldByJavaField(segment); } if (mf == null) { Iterator<MappedClass> subTypes = mapper.getSubTypes(context).iterator(); while (mf == null && subTypes.hasNext()) { context = subTypes.next(); mf = resolveField(segment); } } if (mf != null) { context = mapper.getMappedClass(mf.getSubClass() != null ? mf.getSubClass() : mf.getConcreteType()); } return mf; } @Override public String toString() { return String.format("PathTarget{root=%s, segments=%s, target=%s}", root.getClazz().getSimpleName(), segments, target); } }