private static AbstractConfigObject fromPathMap()

in scripts/src/main/java/com/gu/typesafe/config/impl/PropertiesParser.java [98:209]


    private static AbstractConfigObject fromPathMap(ConfigOrigin origin,
                                                    Map<Path, Object> pathMap, boolean convertedFromProperties) {
        /*
         * First, build a list of paths that will have values, either string or
         * object values.
         */
        Set<Path> scopePaths = new HashSet<Path>();
        Set<Path> valuePaths = new HashSet<Path>();
        for (Path path : pathMap.keySet()) {
            // add value's path
            valuePaths.add(path);

            // all parent paths are objects
            Path next = path.parent();
            while (next != null) {
                scopePaths.add(next);
                next = next.parent();
            }
        }

        if (convertedFromProperties) {
            /*
             * If any string values are also objects containing other values,
             * drop those string values - objects "win".
             */
            valuePaths.removeAll(scopePaths);
        } else {
            /* If we didn't start out as properties, then this is an error. */
            for (Path path : valuePaths) {
                if (scopePaths.contains(path)) {
                    throw new ConfigException.BugOrBroken(
                            "In the map, path '"
                                    + path.render()
                                    + "' occurs as both the parent object of a value and as a value. "
                                    + "Because Map has no defined ordering, this is a broken situation.");
                }
            }
        }

        /*
         * Create maps for the object-valued values.
         */
        Map<String, com.gu.typesafe.config.impl.AbstractConfigValue> root = new HashMap<String, com.gu.typesafe.config.impl.AbstractConfigValue>();
        Map<Path, Map<String, com.gu.typesafe.config.impl.AbstractConfigValue>> scopes = new HashMap<Path, Map<String, com.gu.typesafe.config.impl.AbstractConfigValue>>();

        for (Path path : scopePaths) {
            Map<String, com.gu.typesafe.config.impl.AbstractConfigValue> scope = new HashMap<String, com.gu.typesafe.config.impl.AbstractConfigValue>();
            scopes.put(path, scope);
        }

        /* Store string values in the associated scope maps */
        for (Path path : valuePaths) {
            Path parentPath = path.parent();
            Map<String, com.gu.typesafe.config.impl.AbstractConfigValue> parent = parentPath != null ? scopes
                    .get(parentPath) : root;

            String last = path.last();
            Object rawValue = pathMap.get(path);
            com.gu.typesafe.config.impl.AbstractConfigValue value;
            if (convertedFromProperties) {
                if (rawValue instanceof String) {
                    value = new com.gu.typesafe.config.impl.ConfigString.Quoted(origin, (String) rawValue);
                } else {
                    // silently ignore non-string values in Properties
                    value = null;
                }
            } else {
                value = ConfigImpl.fromAnyRef(pathMap.get(path), origin,
                        FromMapMode.KEYS_ARE_PATHS);
            }
            if (value != null)
                parent.put(last, value);
        }

        /*
         * Make a list of scope paths from longest to shortest, so children go
         * before parents.
         */
        List<Path> sortedScopePaths = new ArrayList<Path>();
        sortedScopePaths.addAll(scopePaths);
        // sort descending by length
        Collections.sort(sortedScopePaths, new Comparator<Path>() {
            @Override
            public int compare(Path a, Path b) {
                // Path.length() is O(n) so in theory this sucks
                // but in practice we can make Path precompute length
                // if it ever matters.
                return b.length() - a.length();
            }
        });

        /*
         * Create ConfigObject for each scope map, working from children to
         * parents to avoid modifying any already-created ConfigObject. This is
         * where we need the sorted list.
         */
        for (Path scopePath : sortedScopePaths) {
            Map<String, com.gu.typesafe.config.impl.AbstractConfigValue> scope = scopes.get(scopePath);

            Path parentPath = scopePath.parent();
            Map<String, com.gu.typesafe.config.impl.AbstractConfigValue> parent = parentPath != null ? scopes
                    .get(parentPath) : root;

            AbstractConfigObject o = new com.gu.typesafe.config.impl.SimpleConfigObject(origin, scope,
                    ResolveStatus.RESOLVED, false /* ignoresFallbacks */);
            parent.put(scopePath.last(), o);
        }

        // return root config object
        return new com.gu.typesafe.config.impl.SimpleConfigObject(origin, root, ResolveStatus.RESOLVED,
                false /* ignoresFallbacks */);
    }