hollow/src/main/java/com/netflix/hollow/core/index/FieldPaths.java
static FieldPath<FieldSegment> createFieldPath(
            HollowDataset dataset, String type, String path,
            boolean autoExpand, boolean requireFullPath, boolean traverseSequences) {
        Objects.requireNonNull(dataset);
        Objects.requireNonNull(type);
        Objects.requireNonNull(path);

        String[] segments = path.isEmpty() ? new String[0] : path.split("\\.");
        List<FieldSegment> fieldSegments = new ArrayList<>();

        String segmentType = type;
        for (int i = 0; i < segments.length; i++) {
            HollowSchema schema = dataset.getSchema(segmentType);
            // @@@ Can this only occur for anything other than the root `type`?
            if (schema == null) {
                throw new FieldPathException(FieldPathException.ErrorKind.NOT_BINDABLE, dataset, type, segments,
                        fieldSegments, null, i);
            }

            String segment = segments[i];
            HollowSchema.SchemaType schemaType = schema.getSchemaType();
            if (schemaType == HollowSchema.SchemaType.OBJECT) {
                HollowObjectSchema objectSchema = (HollowObjectSchema) schema;

                int index = objectSchema.getPosition(segment);
                if (index == -1) {
                    throw new FieldPathException(FieldPathException.ErrorKind.NOT_FOUND, dataset, type, segments,
                            fieldSegments, schema, i);
                }

                segmentType = objectSchema.getReferencedType(index);
                fieldSegments.add(new ObjectFieldSegment(objectSchema, segment, segmentType, index));
            } else if (traverseSequences && (schemaType == HollowSchema.SchemaType.SET
                    || schemaType == HollowSchema.SchemaType.LIST)) {
                HollowCollectionSchema collectionSchema = (HollowCollectionSchema) schema;

                if (!segment.equals("element")) {
                    throw new FieldPathException(FieldPathException.ErrorKind.NOT_FOUND, dataset, type, segments,
                            fieldSegments, schema, i);
                }

                segmentType = collectionSchema.getElementType();
                fieldSegments.add(new FieldSegment(collectionSchema, segment, segmentType));
            } else if (traverseSequences && schemaType == HollowSchema.SchemaType.MAP) {
                HollowMapSchema mapSchema = (HollowMapSchema) schema;

                if (segment.equals("key")) {
                    segmentType = mapSchema.getKeyType();
                } else if (segment.equals("value")) {
                    segmentType = mapSchema.getValueType();
                } else {
                    throw new FieldPathException(FieldPathException.ErrorKind.NOT_FOUND, dataset, type, segments,
                            fieldSegments, schema, i);
                }

                fieldSegments.add(new FieldSegment(mapSchema, segment, segmentType));
            } else if (!traverseSequences) {
                throw new FieldPathException(FieldPathException.ErrorKind.NOT_TRAVERSABLE, dataset, type, segments,
                        fieldSegments, schema, i);
            }

            if (i < segments.length - 1 && segmentType == null) {
                throw new FieldPathException(FieldPathException.ErrorKind.NOT_TRAVERSABLE, dataset, type, segments,
                        fieldSegments, schema, i);
            }
        }

        if (autoExpand) {
            while (segmentType != null) {
                HollowSchema schema = dataset.getSchema(segmentType);

                if (schema.getSchemaType() == HollowSchema.SchemaType.OBJECT) {
                    HollowObjectSchema objectSchema = (HollowObjectSchema) schema;

                    if (objectSchema.numFields() == 1) {
                        segmentType = objectSchema.getReferencedType(0);

                        fieldSegments.add(
                                new ObjectFieldSegment(objectSchema, objectSchema.getFieldName(0), segmentType,
                                        0));
                    } else if (objectSchema.getPrimaryKey() != null && objectSchema.getPrimaryKey().numFields() == 1) {
                        PrimaryKey key = objectSchema.getPrimaryKey();

                        FieldPath<ObjectFieldSegment> expandedFieldSegments;
                        try {
                            expandedFieldSegments =
                                    createFieldPathForPrimaryKey(dataset, key.getType(), key.getFieldPaths()[0]);
                        } catch (FieldPathException cause) {
                            FieldPathException e = new FieldPathException(FieldPathException.ErrorKind.NOT_EXPANDABLE,
                                    dataset, type, segments,
                                    fieldSegments, objectSchema);
                            e.initCause(cause);
                            throw e;
                        }

                        fieldSegments.addAll(expandedFieldSegments.segments);
                        break;
                    } else {
                        throw new FieldPathException(FieldPathException.ErrorKind.NOT_EXPANDABLE, dataset, type,
                                segments,
                                fieldSegments, objectSchema);
                    }
                } else {
                    throw new FieldPathException(FieldPathException.ErrorKind.NOT_EXPANDABLE, dataset, type, segments,
                            fieldSegments, schema);
                }
            }
        } else if (requireFullPath && segmentType != null) {
            throw new FieldPathException(FieldPathException.ErrorKind.NOT_FULL, dataset, type, segments,
                    fieldSegments);
        }

        return new FieldPath<>(type, fieldSegments, !autoExpand);
    }
static FieldPath<FieldSegment> createFieldPath(
            HollowDataset dataset, String type, String path,
            boolean autoExpand, boolean requireFullPath, boolean traverseSequences) {
        Objects.requireNonNull(dataset);
        Objects.requireNonNull(type);
        Objects.requireNonNull(path);

        String[] segments = path.isEmpty() ? new String[0] : path.split("\\.");
        List<FieldSegment> fieldSegments = new ArrayList<>();

        String segmentType = type;
        for (int i = 0; i < segments.length; i++) {
            HollowSchema schema = dataset.getSchema(segmentType);
            // @@@ Can this only occur for anything other than the root `type`?
            if (schema == null) {
                throw new FieldPathException(FieldPathException.ErrorKind.NOT_BINDABLE, dataset, type, segments,
                        fieldSegments, null, i);
            }

            String segment = segments[i];
            HollowSchema.SchemaType schemaType = schema.getSchemaType();
            if (schemaType == HollowSchema.SchemaType.OBJECT) {
                HollowObjectSchema objectSchema = (HollowObjectSchema) schema;

                int index = objectSchema.getPosition(segment);
                if (index == -1) {
                    throw new FieldPathException(FieldPathException.ErrorKind.NOT_FOUND, dataset, type, segments,
                            fieldSegments, schema, i);
                }

                segmentType = objectSchema.getReferencedType(index);
                fieldSegments.add(new ObjectFieldSegment(objectSchema, segment, segmentType, index));
            } else if (traverseSequences && (schemaType == HollowSchema.SchemaType.SET
                    || schemaType == HollowSchema.SchemaType.LIST)) {
                HollowCollectionSchema collectionSchema = (HollowCollectionSchema) schema;

                if (!segment.equals("element")) {
                    throw new FieldPathException(FieldPathException.ErrorKind.NOT_FOUND, dataset, type, segments,
                            fieldSegments, schema, i);
                }

                segmentType = collectionSchema.getElementType();
                fieldSegments.add(new FieldSegment(collectionSchema, segment, segmentType));
            } else if (traverseSequences && schemaType == HollowSchema.SchemaType.MAP) {
                HollowMapSchema mapSchema = (HollowMapSchema) schema;

                if (segment.equals("key")) {
                    segmentType = mapSchema.getKeyType();
                } else if (segment.equals("value")) {
                    segmentType = mapSchema.getValueType();
                } else {
                    throw new FieldPathException(FieldPathException.ErrorKind.NOT_FOUND, dataset, type, segments,
                            fieldSegments, schema, i);
                }

                fieldSegments.add(new FieldSegment(mapSchema, segment, segmentType));
            } else if (!traverseSequences) {
                throw new FieldPathException(FieldPathException.ErrorKind.NOT_TRAVERSABLE, dataset, type, segments,
                        fieldSegments, schema, i);
            }

            if (i < segments.length - 1 && segmentType == null) {
                throw new FieldPathException(FieldPathException.ErrorKind.NOT_TRAVERSABLE, dataset, type, segments,
                        fieldSegments, schema, i);
            }
        }

        if (autoExpand) {
            while (segmentType != null) {
                HollowSchema schema = dataset.getSchema(segmentType);

                if (schema.getSchemaType() == HollowSchema.SchemaType.OBJECT) {
                    HollowObjectSchema objectSchema = (HollowObjectSchema) schema;

                    if (objectSchema.numFields() == 1) {
                        segmentType = objectSchema.getReferencedType(0);

                        fieldSegments.add(
                                new ObjectFieldSegment(objectSchema, objectSchema.getFieldName(0), segmentType,
                                        0));
                    } else if (objectSchema.getPrimaryKey() != null && objectSchema.getPrimaryKey().numFields() == 1) {
                        PrimaryKey key = objectSchema.getPrimaryKey();

                        FieldPath<ObjectFieldSegment> expandedFieldSegments;
                        try {
                            expandedFieldSegments =
                                    createFieldPathForPrimaryKey(dataset, key.getType(), key.getFieldPaths()[0]);
                        } catch (FieldPathException cause) {
                            FieldPathException e = new FieldPathException(FieldPathException.ErrorKind.NOT_EXPANDABLE,
                                    dataset, type, segments,
                                    fieldSegments, objectSchema);
                            e.initCause(cause);
                            throw e;
                        }

                        fieldSegments.addAll(expandedFieldSegments.segments);
                        break;
                    } else {
                        throw new FieldPathException(FieldPathException.ErrorKind.NOT_EXPANDABLE, dataset, type,
                                segments,
                                fieldSegments, objectSchema);
                    }
                } else {
                    throw new FieldPathException(FieldPathException.ErrorKind.NOT_EXPANDABLE, dataset, type, segments,
                            fieldSegments, schema);
                }
            }
        } else if (requireFullPath && segmentType != null) {
            throw new FieldPathException(FieldPathException.ErrorKind.NOT_FULL, dataset, type, segments,
                    fieldSegments);
        }

        return new FieldPath<>(type, fieldSegments, !autoExpand);
    }
Creates a field path given a data set and the field path in symbolic form.

@param dataset the data set
@param type the type name from which to bind the field path
@param path the symbolic field path
@param autoExpand {@code true} if the field path should be expanded (if needed) to a full path referencing
an object field whose type is a non-reference type, otherwise the field path is not expanded
@param requireFullPath {@code true} if a full path is required when {@code autoExpand} is {@code false}.
Ignored if {@code autoExpand} is {@code true}.
@param traverseSequences {@code true} if lists, sets and maps are traversed, otherwise an
{@code IllegalArgumentException} will be thrown
@return the field path
@throws IllegalArgumentException if the symbolic field path is ill-formed and cannot be bound