public void visitDot()

in modules/lang-painless/src/main/java/org/elasticsearch/painless/phase/DefaultSemanticAnalysisPhase.java [2677:3031]


    public void visitDot(EDot userDotNode, SemanticScope semanticScope) {
        boolean read = semanticScope.getCondition(userDotNode, Read.class);
        boolean write = semanticScope.getCondition(userDotNode, Write.class);

        if (read == false && write == false) {
            throw userDotNode.createError(new IllegalArgumentException("not a statement: result of dot operator [.] not used"));
        }

        ScriptScope scriptScope = semanticScope.getScriptScope();
        String index = userDotNode.getIndex();

        AExpression userPrefixNode = userDotNode.getPrefixNode();
        semanticScope.setCondition(userPrefixNode, Read.class);
        visit(userPrefixNode, semanticScope);
        ValueType prefixValueType = semanticScope.getDecoration(userPrefixNode, ValueType.class);
        StaticType prefixStaticType = semanticScope.getDecoration(userPrefixNode, StaticType.class);

        if (prefixValueType != null && prefixStaticType != null) {
            throw userDotNode.createError(
                new IllegalStateException(
                    Strings.format(
                        "cannot have both value [%s] and type [%s]",
                        prefixValueType.getValueCanonicalTypeName(),
                        prefixStaticType.getStaticCanonicalTypeName()
                    )
                )
            );
        }

        if (semanticScope.hasDecoration(userPrefixNode, PartialCanonicalTypeName.class)) {
            if (prefixValueType != null) {
                throw userDotNode.createError(
                    new IllegalArgumentException(
                        "value required: instead found unexpected type " + "[" + prefixValueType.getValueCanonicalTypeName() + "]"
                    )
                );
            }

            if (prefixStaticType != null) {
                throw userDotNode.createError(
                    new IllegalArgumentException(
                        "value required: instead found unexpected type " + "[" + prefixStaticType.staticType() + "]"
                    )
                );
            }

            String canonicalTypeName = semanticScope.getDecoration(userPrefixNode, PartialCanonicalTypeName.class)
                .partialCanonicalTypeName()
                + "."
                + index;
            Class<?> staticType = scriptScope.getPainlessLookup().canonicalTypeNameToType(canonicalTypeName);

            if (staticType == null) {
                semanticScope.putDecoration(userDotNode, new PartialCanonicalTypeName(canonicalTypeName));
            } else {
                if (write) {
                    throw userDotNode.createError(
                        new IllegalArgumentException(
                            "invalid assignment: "
                                + "cannot write a value to a static type ["
                                + PainlessLookupUtility.typeToCanonicalTypeName(staticType)
                                + "]"
                        )
                    );
                }

                semanticScope.putDecoration(userDotNode, new StaticType(staticType));
            }
        } else {
            Class<?> staticType = null;

            if (prefixStaticType != null) {
                String staticCanonicalTypeName = prefixStaticType.getStaticCanonicalTypeName() + "." + userDotNode.getIndex();
                staticType = scriptScope.getPainlessLookup().canonicalTypeNameToType(staticCanonicalTypeName);
            }

            if (staticType != null) {
                if (write) {
                    throw userDotNode.createError(
                        new IllegalArgumentException(
                            "invalid assignment: "
                                + "cannot write a value to a static type ["
                                + PainlessLookupUtility.typeToCanonicalTypeName(staticType)
                                + "]"
                        )
                    );
                }

                semanticScope.putDecoration(userDotNode, new StaticType(staticType));
            } else {
                Class<?> valueType = null;

                if (prefixValueType != null && prefixValueType.valueType().isArray()) {
                    if ("length".equals(index)) {
                        if (write) {
                            throw userDotNode.createError(
                                new IllegalArgumentException(
                                    "invalid assignment: cannot assign a value write to read-only field [length] for an array."
                                )
                            );
                        }

                        valueType = int.class;
                    } else {
                        throw userDotNode.createError(
                            new IllegalArgumentException(
                                "Field [" + index + "] does not exist for type [" + prefixValueType.getValueCanonicalTypeName() + "]."
                            )
                        );
                    }
                } else if (prefixValueType != null && prefixValueType.valueType() == def.class) {
                    TargetType targetType = userDotNode.isNullSafe() ? null : semanticScope.getDecoration(userDotNode, TargetType.class);
                    valueType = targetType == null || semanticScope.getCondition(userDotNode, Explicit.class)
                        ? def.class
                        : targetType.targetType();

                    if (write) {
                        semanticScope.setCondition(userDotNode, DefOptimized.class);
                    }
                } else {
                    Class<?> prefixType;
                    String prefixCanonicalTypeName;
                    boolean isStatic;

                    if (prefixValueType != null) {
                        prefixType = prefixValueType.valueType();
                        prefixCanonicalTypeName = prefixValueType.getValueCanonicalTypeName();
                        isStatic = false;
                    } else if (prefixStaticType != null) {
                        prefixType = prefixStaticType.staticType();
                        prefixCanonicalTypeName = prefixStaticType.getStaticCanonicalTypeName();
                        isStatic = true;
                    } else {
                        throw userDotNode.createError(new IllegalStateException("value required: instead found no value"));
                    }

                    PainlessField field = semanticScope.getScriptScope()
                        .getPainlessLookup()
                        .lookupPainlessField(prefixType, isStatic, index);

                    if (field == null) {
                        PainlessMethod getter;
                        PainlessMethod setter;

                        getter = scriptScope.getPainlessLookup()
                            .lookupPainlessMethod(
                                prefixType,
                                isStatic,
                                "get" + Character.toUpperCase(index.charAt(0)) + index.substring(1),
                                0
                            );

                        if (getter == null) {
                            getter = scriptScope.getPainlessLookup()
                                .lookupPainlessMethod(
                                    prefixType,
                                    isStatic,
                                    "is" + Character.toUpperCase(index.charAt(0)) + index.substring(1),
                                    0
                                );
                        }

                        setter = scriptScope.getPainlessLookup()
                            .lookupPainlessMethod(
                                prefixType,
                                isStatic,
                                "set" + Character.toUpperCase(index.charAt(0)) + index.substring(1),
                                1
                            );

                        if (getter != null || setter != null) {
                            if (getter != null && (getter.returnType() == void.class || getter.typeParameters().isEmpty() == false)) {
                                throw userDotNode.createError(
                                    new IllegalArgumentException(
                                        "Illegal get shortcut on field [" + index + "] for type [" + prefixCanonicalTypeName + "]."
                                    )
                                );
                            }

                            if (setter != null && (setter.returnType() != void.class || setter.typeParameters().size() != 1)) {
                                throw userDotNode.createError(
                                    new IllegalArgumentException(
                                        "Illegal set shortcut on field [" + index + "] for type [" + prefixCanonicalTypeName + "]."
                                    )
                                );
                            }

                            if (getter != null && setter != null && setter.typeParameters().get(0) != getter.returnType()) {
                                throw userDotNode.createError(new IllegalArgumentException("Shortcut argument types must match."));
                            }

                            if ((read == false || getter != null) && (write == false || setter != null)) {
                                valueType = setter != null ? setter.typeParameters().get(0) : getter.returnType();
                            } else {
                                throw userDotNode.createError(
                                    new IllegalArgumentException(
                                        "Illegal shortcut on field [" + index + "] for type [" + prefixCanonicalTypeName + "]."
                                    )
                                );
                            }

                            if (getter != null) {
                                semanticScope.putDecoration(userDotNode, new GetterPainlessMethod(getter));
                            }

                            if (setter != null) {
                                semanticScope.putDecoration(userDotNode, new SetterPainlessMethod(setter));
                            }

                            semanticScope.setCondition(userDotNode, Shortcut.class);
                        } else if (isStatic == false) {
                            if (Map.class.isAssignableFrom(prefixValueType.valueType())) {
                                getter = scriptScope.getPainlessLookup().lookupPainlessMethod(prefixType, false, "get", 1);
                                setter = scriptScope.getPainlessLookup().lookupPainlessMethod(prefixType, false, "put", 2);

                                if (getter != null && (getter.returnType() == void.class || getter.typeParameters().size() != 1)) {
                                    throw userDotNode.createError(
                                        new IllegalArgumentException("Illegal map get shortcut for type [" + prefixCanonicalTypeName + "].")
                                    );
                                }

                                if (setter != null && setter.typeParameters().size() != 2) {
                                    throw userDotNode.createError(
                                        new IllegalArgumentException("Illegal map set shortcut for type [" + prefixCanonicalTypeName + "].")
                                    );
                                }

                                if (getter != null
                                    && setter != null
                                    && (getter.typeParameters().get(0).equals(setter.typeParameters().get(0)) == false
                                        || getter.returnType().equals(setter.typeParameters().get(1)) == false)) {
                                    throw userDotNode.createError(new IllegalArgumentException("Shortcut argument types must match."));
                                }

                                if ((read == false || getter != null) && (write == false || setter != null)) {
                                    valueType = setter != null ? setter.typeParameters().get(1) : getter.returnType();
                                } else {
                                    throw userDotNode.createError(
                                        new IllegalArgumentException("Illegal map shortcut for type [" + prefixCanonicalTypeName + "].")
                                    );
                                }

                                if (getter != null) {
                                    semanticScope.putDecoration(userDotNode, new GetterPainlessMethod(getter));
                                }

                                if (setter != null) {
                                    semanticScope.putDecoration(userDotNode, new SetterPainlessMethod(setter));
                                }

                                semanticScope.setCondition(userDotNode, MapShortcut.class);
                            }

                            if (List.class.isAssignableFrom(prefixType)) {
                                try {
                                    scriptScope.putDecoration(userDotNode, new StandardConstant(Integer.parseInt(index)));
                                } catch (NumberFormatException nfe) {
                                    throw userDotNode.createError(new IllegalArgumentException("invalid list index [" + index + "]", nfe));
                                }

                                getter = scriptScope.getPainlessLookup().lookupPainlessMethod(prefixType, false, "get", 1);
                                setter = scriptScope.getPainlessLookup().lookupPainlessMethod(prefixType, false, "set", 2);

                                if (getter != null
                                    && (getter.returnType() == void.class
                                        || getter.typeParameters().size() != 1
                                        || getter.typeParameters().get(0) != int.class)) {
                                    throw userDotNode.createError(
                                        new IllegalArgumentException(
                                            "Illegal list get shortcut for type [" + prefixCanonicalTypeName + "]."
                                        )
                                    );
                                }

                                if (setter != null
                                    && (setter.typeParameters().size() != 2 || setter.typeParameters().get(0) != int.class)) {
                                    throw userDotNode.createError(
                                        new IllegalArgumentException(
                                            "Illegal list set shortcut for type [" + prefixCanonicalTypeName + "]."
                                        )
                                    );
                                }

                                if (getter != null
                                    && setter != null
                                    && (getter.typeParameters().get(0).equals(setter.typeParameters().get(0)) == false
                                        || getter.returnType().equals(setter.typeParameters().get(1)) == false)) {
                                    throw userDotNode.createError(new IllegalArgumentException("Shortcut argument types must match."));
                                }

                                if ((read == false || getter != null) && (write == false || setter != null)) {
                                    valueType = setter != null ? setter.typeParameters().get(1) : getter.returnType();
                                } else {
                                    throw userDotNode.createError(
                                        new IllegalArgumentException("Illegal list shortcut for type [" + prefixCanonicalTypeName + "].")
                                    );
                                }

                                if (getter != null) {
                                    semanticScope.putDecoration(userDotNode, new GetterPainlessMethod(getter));
                                }

                                if (setter != null) {
                                    semanticScope.putDecoration(userDotNode, new SetterPainlessMethod(setter));
                                }

                                semanticScope.setCondition(userDotNode, ListShortcut.class);
                            }
                        }

                        if (valueType == null) {
                            if (prefixValueType != null) {
                                throw userDotNode.createError(
                                    new IllegalArgumentException(
                                        "field [" + prefixValueType.getValueCanonicalTypeName() + ", " + index + "] not found"
                                    )
                                );
                            } else {
                                throw userDotNode.createError(
                                    new IllegalArgumentException(
                                        "field [" + prefixStaticType.getStaticCanonicalTypeName() + ", " + index + "] not found"
                                    )
                                );
                            }
                        }
                    } else {
                        if (write && Modifier.isFinal(field.javaField().getModifiers())) {
                            throw userDotNode.createError(
                                new IllegalArgumentException(
                                    "invalid assignment: cannot assign a value to read-only field [" + field.javaField().getName() + "]"
                                )
                            );
                        }

                        semanticScope.putDecoration(userDotNode, new StandardPainlessField(field));
                        valueType = field.typeParameter();
                    }
                }

                semanticScope.putDecoration(userDotNode, new ValueType(valueType));

                if (userDotNode.isNullSafe()) {
                    if (write) {
                        throw userDotNode.createError(
                            new IllegalArgumentException("invalid assignment: cannot assign a value to a null safe operation [?.]")
                        );
                    }

                    if (valueType.isPrimitive()) {
                        throw new IllegalArgumentException("Result of null safe operator must be nullable");
                    }
                }
            }
        }
    }