Search code examples
javayamlsnakeyaml

Infinite loop while reading yaml file because of pointers


I'm reading and writing YML content into files. Some files are like that:

&id001
playername: SomeName
lang: fr_FR
minerate-full-mined: *id001

The issue is that the minerate-full-mined key refer to the first line of file, so it make an infinite loop.

My code:

DumperOptions options = new DumperOptions();
options.setDefaultFlowStyle(DumperOptions.FlowStyle.BLOCK);
Yaml yaml = new Yaml(new Representer(options) {
    {
        this.representers.put(Configuration.class, (data) -> represent(((Configuration) data).self));
    }
}, options);
protected Map<String, Object> self;
protected Configuration defaults;
protected File file;

public Configuration() {
    this(null);
}

public Configuration(Configuration defaults) {
    this(null, new LinkedHashMap<String, Object>(), defaults);
}

public Configuration(File file, Map<?, ?> map, Configuration defaults) {
    this.file = file;
    this.self = new LinkedHashMap<String, Object>();
    this.defaults = defaults;
    for (Map.Entry<?, ?> entry : map.entrySet()) {
        String key = (entry.getKey() == null) ? "null" : entry.getKey().toString();
        if (entry.getValue() instanceof Map) {
            this.self.put(key, new Configuration(file, (Map<?, ?>) entry.getValue(), defaults == null ? null : defaults.getSection(key)));
        } else {
            this.self.put(key, entry.getValue());
        }
    }
}

The infinite loop is made at the line this.self.put(key, new Configuration[...]);.

I tried to print entry.getValue(), and it's printed as {playername: SomeName, lang: fr_FR, minerate-full-mined=(This Map)}. So it can be detected, but I don't know that we can put a map on itself.

How can I prevent snakeyaml to take this in count ?


Solution

  • I made a trick to skip it and prevent this type of issue, like that:

    Configuration(File file, Map<?, ?> map, Configuration defaults) {
        this.file = file;
        this.self = new LinkedHashMap<String, Object>();
        this.defaults = defaults;
        for (Map.Entry<?, ?> entry : map.entrySet()) {
            String key = (entry.getKey() == null) ? "null" : entry.getKey().toString();
            if (entry.getValue() instanceof Map) {
                Configuration nextDef = defaults == null ? null : defaults.getSection(key);
                String checkingStr = entry.getValue().toString();
                if (checkingStr.contains(key + "=(this Map)") || checkingStr.contains(key + "=(this Collection)"))
                    continue;
                this.self.put(key, new Configuration(file, (Map<?, ?>) entry.getValue(), nextDef == this ? null : nextDef)); // prevent infinite loop
            } else {
                this.self.put(key, entry.getValue());
            }
        }
    }
    

    With checkingStr.contains(key + "=(this Map)") || checkingStr.contains(key + "=(this Collection)") I can skip all map to be added into themself.