1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34 package info.magnolia.config.source.yaml;
35
36 import info.magnolia.resourceloader.Resource;
37
38 import java.io.IOException;
39 import java.io.Reader;
40 import java.util.Collections;
41 import java.util.Map;
42
43 import javax.inject.Singleton;
44
45 import org.slf4j.Logger;
46 import org.slf4j.LoggerFactory;
47 import org.yaml.snakeyaml.Yaml;
48 import org.yaml.snakeyaml.constructor.AbstractConstruct;
49 import org.yaml.snakeyaml.constructor.Constructor;
50 import org.yaml.snakeyaml.error.YAMLException;
51 import org.yaml.snakeyaml.nodes.Node;
52 import org.yaml.snakeyaml.nodes.ScalarNode;
53 import org.yaml.snakeyaml.nodes.Tag;
54
55
56
57
58 @Singleton
59 public class YamlReader {
60
61 private static final Logger log = LoggerFactory.getLogger(YamlReader.class);
62
63 public Object read(Resource res) throws IOException {
64 return readNoCast(res);
65 }
66
67 @SuppressWarnings("unchecked")
68 public Map<String, Object> readToMap(final Resource resource) throws IOException {
69 return (Map<String, Object>) read(resource);
70 }
71
72 protected Object readNoCast(final Resource res) throws IOException {
73 final Yaml yaml = newSnakeYaml(res);
74 try (final Reader reader = res.openReader()) {
75 return yaml.load(new WhiteSpaceNormalisingReader(reader));
76 } catch (YAMLException ye) {
77 throw new YamlReaderException(ye, res);
78 }
79 }
80
81 protected Yaml newSnakeYaml(Resource res) {
82 return new Yaml(new CustomConstructor(this, res));
83 }
84
85 private static class CustomConstructor extends Constructor {
86 public CustomConstructor(YamlReader yamlReader, Resource baseResource) {
87 yamlConstructors.put(new Tag("!include"), new IncludeConstruct(yamlReader, baseResource));
88 }
89 }
90
91 private static class IncludeConstruct extends AbstractConstruct {
92 private final YamlReader yamlReader;
93 private final Resource baseResource;
94
95 public IncludeConstruct(YamlReader yamlReader, Resource baseResource) {
96 this.yamlReader = yamlReader;
97 this.baseResource = baseResource;
98 }
99
100 @Override
101 public Object construct(Node node) {
102 if (!(node instanceof ScalarNode)) {
103 throw new IllegalArgumentException("Non-scalar !include: " + node.toString());
104 }
105
106 final ScalarNode scalarNode = (ScalarNode) node;
107 final String path = scalarNode.getValue();
108
109 final Resource include = baseResource.getOrigin().getByPath(path);
110 try {
111 return yamlReader.readNoCast(include);
112 } catch (IOException e) {
113 log.error("Failed to process included Yaml resource at [{}] included into [{}] due to: {}. Returning empty map", include.getPath(), baseResource.getPath(), e.getMessage(), e);
114 return Collections.emptyMap();
115 }
116 }
117 }
118 }