protected ConfigNodeObject addValueOnPath()

in scripts/src/main/java/com/gu/typesafe/config/impl/ConfigNodeObject.java [166:275]


    protected ConfigNodeObject addValueOnPath(com.gu.typesafe.config.impl.ConfigNodePath desiredPath, com.gu.typesafe.config.impl.AbstractConfigNodeValue value, com.gu.typesafe.config.ConfigSyntax flavor) {
        Path path = desiredPath.value();
        ArrayList<AbstractConfigNode> childrenCopy = new ArrayList<AbstractConfigNode>(super.children);
        ArrayList<AbstractConfigNode> indentation = new ArrayList<AbstractConfigNode>(indentation());

        // If the value we're inserting is a complex value, we'll need to indent it for insertion
        com.gu.typesafe.config.impl.AbstractConfigNodeValue indentedValue;
        if (value instanceof com.gu.typesafe.config.impl.ConfigNodeComplexValue && !indentation.isEmpty()) {
            indentedValue = ((com.gu.typesafe.config.impl.ConfigNodeComplexValue) value).indentText(indentation.get(indentation.size() - 1));
        } else {
            indentedValue = value;
        }
        boolean sameLine = !(indentation.size() > 0 && indentation.get(0) instanceof com.gu.typesafe.config.impl.ConfigNodeSingleToken &&
                                com.gu.typesafe.config.impl.Tokens.isNewline(((com.gu.typesafe.config.impl.ConfigNodeSingleToken) indentation.get(0)).token()));

        // If the path is of length greater than one, see if the value needs to be added further down
        if (path.length() > 1) {
            for (int i = super.children.size() - 1; i >= 0; i--) {
                if (!(super.children.get(i) instanceof ConfigNodeField)) {
                    continue;
                }
                ConfigNodeField node = (ConfigNodeField) super.children.get(i);
                Path key = node.path().value();
                if (path.startsWith(key) && node.value() instanceof ConfigNodeObject) {
                    com.gu.typesafe.config.impl.ConfigNodePath remainingPath = desiredPath.subPath(key.length());
                    ConfigNodeObject newValue = (ConfigNodeObject) node.value();
                    childrenCopy.set(i, node.replaceValue(newValue.addValueOnPath(remainingPath, value, flavor)));
                    return new ConfigNodeObject(childrenCopy);
                }
            }
        }

        // Otherwise, construct the new setting
        boolean startsWithBrace = !super.children.isEmpty() && super.children.get(0) instanceof com.gu.typesafe.config.impl.ConfigNodeSingleToken &&
                ((com.gu.typesafe.config.impl.ConfigNodeSingleToken) super.children.get(0)).token() == com.gu.typesafe.config.impl.Tokens.OPEN_CURLY;
        ArrayList<AbstractConfigNode> newNodes = new ArrayList<AbstractConfigNode>();
        newNodes.addAll(indentation);
        newNodes.add(desiredPath.first());
        newNodes.add(new com.gu.typesafe.config.impl.ConfigNodeSingleToken(com.gu.typesafe.config.impl.Tokens.newIgnoredWhitespace(null, " ")));
        newNodes.add(new com.gu.typesafe.config.impl.ConfigNodeSingleToken(com.gu.typesafe.config.impl.Tokens.COLON));
        newNodes.add(new com.gu.typesafe.config.impl.ConfigNodeSingleToken(com.gu.typesafe.config.impl.Tokens.newIgnoredWhitespace(null, " ")));

        if (path.length() == 1) {
            newNodes.add(indentedValue);
        } else {
            // If the path is of length greater than one add the required new objects along the path
            ArrayList<AbstractConfigNode> newObjectNodes = new ArrayList<AbstractConfigNode>();
            newObjectNodes.add(new com.gu.typesafe.config.impl.ConfigNodeSingleToken(com.gu.typesafe.config.impl.Tokens.OPEN_CURLY));
            if (indentation.isEmpty()) {
                newObjectNodes.add(new com.gu.typesafe.config.impl.ConfigNodeSingleToken(com.gu.typesafe.config.impl.Tokens.newLine(null)));
            }
            newObjectNodes.addAll(indentation);
            newObjectNodes.add(new com.gu.typesafe.config.impl.ConfigNodeSingleToken(com.gu.typesafe.config.impl.Tokens.CLOSE_CURLY));
            ConfigNodeObject newObject = new ConfigNodeObject(newObjectNodes);
            newNodes.add(newObject.addValueOnPath(desiredPath.subPath(1), indentedValue, flavor));
        }

        // Combine these two cases so that we only have to iterate once
        if (flavor == com.gu.typesafe.config.ConfigSyntax.JSON || startsWithBrace || sameLine) {
            for (int i = childrenCopy.size() - 1; i >= 0; i--) {

                // If we are in JSON or are adding a setting on the same line, we need to add a comma to the
                // last setting
                if ((flavor == com.gu.typesafe.config.ConfigSyntax.JSON || sameLine) && childrenCopy.get(i) instanceof ConfigNodeField) {
                    if (i+1 >= childrenCopy.size() ||
                            !(childrenCopy.get(i+1) instanceof com.gu.typesafe.config.impl.ConfigNodeSingleToken
                                    && ((com.gu.typesafe.config.impl.ConfigNodeSingleToken) childrenCopy.get(i+1)).token() == com.gu.typesafe.config.impl.Tokens.COMMA))
                    childrenCopy.add(i+1, new com.gu.typesafe.config.impl.ConfigNodeSingleToken(com.gu.typesafe.config.impl.Tokens.COMMA));
                    break;
                }

                // Add the value into the copy of the children map, keeping any whitespace/newlines
                // before the close curly brace
                if (startsWithBrace && childrenCopy.get(i) instanceof com.gu.typesafe.config.impl.ConfigNodeSingleToken &&
                        ((com.gu.typesafe.config.impl.ConfigNodeSingleToken) childrenCopy.get(i)).token == com.gu.typesafe.config.impl.Tokens.CLOSE_CURLY) {
                    AbstractConfigNode previous = childrenCopy.get(i - 1);
                    if (previous instanceof com.gu.typesafe.config.impl.ConfigNodeSingleToken &&
                            com.gu.typesafe.config.impl.Tokens.isNewline(((com.gu.typesafe.config.impl.ConfigNodeSingleToken) previous).token())) {
                        childrenCopy.add(i - 1, new ConfigNodeField(newNodes));
                        i--;
                    } else if (previous instanceof com.gu.typesafe.config.impl.ConfigNodeSingleToken &&
                                com.gu.typesafe.config.impl.Tokens.isIgnoredWhitespace(((com.gu.typesafe.config.impl.ConfigNodeSingleToken) previous).token())) {
                        AbstractConfigNode beforePrevious = childrenCopy.get(i - 2);
                        if (sameLine) {
                            childrenCopy.add(i - 1, new ConfigNodeField(newNodes));
                            i--;
                        }
                        else if (beforePrevious instanceof com.gu.typesafe.config.impl.ConfigNodeSingleToken &&
                                    com.gu.typesafe.config.impl.Tokens.isNewline(((com.gu.typesafe.config.impl.ConfigNodeSingleToken) beforePrevious).token())) {
                            childrenCopy.add(i - 2, new ConfigNodeField(newNodes));
                            i -= 2;
                        } else {
                            childrenCopy.add(i, new ConfigNodeField(newNodes));
                        }

                    }
                    else
                        childrenCopy.add(i, new ConfigNodeField(newNodes));
                }
            }
        }
        if (!startsWithBrace) {
            if (!childrenCopy.isEmpty() && childrenCopy.get(childrenCopy.size() - 1) instanceof com.gu.typesafe.config.impl.ConfigNodeSingleToken &&
                 com.gu.typesafe.config.impl.Tokens.isNewline(((com.gu.typesafe.config.impl.ConfigNodeSingleToken) childrenCopy.get(childrenCopy.size() - 1)).token()))
                childrenCopy.add(childrenCopy.size() - 1, new ConfigNodeField(newNodes));
            else
                childrenCopy.add(new ConfigNodeField(newNodes));
        }
        return new ConfigNodeObject(childrenCopy);
    }