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.Map;
41
42 import javax.inject.Singleton;
43
44 import org.yaml.snakeyaml.Yaml;
45 import org.yaml.snakeyaml.constructor.AbstractConstruct;
46 import org.yaml.snakeyaml.constructor.Constructor;
47 import org.yaml.snakeyaml.error.Mark;
48 import org.yaml.snakeyaml.error.MarkedYAMLException;
49 import org.yaml.snakeyaml.error.YAMLException;
50 import org.yaml.snakeyaml.nodes.Node;
51 import org.yaml.snakeyaml.nodes.ScalarNode;
52 import org.yaml.snakeyaml.nodes.Tag;
53
54
55
56
57 @Singleton
58 public class YamlReader {
59
60 public YamlReader() {
61 }
62
63 public Map<String, Object> readToMap(final Resource res) throws IOException {
64 return (Map<String, Object>) readNoCast(res);
65 }
66
67 protected Object readNoCast(final Resource res) throws IOException {
68 final Yaml yaml = newSnakeYaml(res);
69 try (final Reader reader = res.openReader()) {
70 return yaml.load(new SpaceCleaningReader(reader));
71 } catch (YAMLException ye) {
72
73 throw new RuntimeException(formatYamlExceptionMessage(res, ye), ye);
74 }
75 }
76
77 protected Yaml newSnakeYaml(Resource res) {
78 return new Yaml(new CustomConstructor(this, res));
79 }
80
81 protected String formatYamlExceptionMessage(Resource res, YAMLException ye) {
82 final StringBuilder sb = new StringBuilder();
83 sb.append("YAML parsing error in ").append(res);
84 if (ye instanceof MarkedYAMLException) {
85 final Mark mark = ((MarkedYAMLException) ye).getProblemMark();
86 final String snippet = mark != null ? mark.get_snippet() : null;
87 final String problem = ((MarkedYAMLException) ye).getProblem();
88 if (mark != null) {
89 sb.append(" at line ").append(mark.getLine()).append(", column ").append(mark.getColumn());
90 }
91 if (snippet != null) {
92 sb.append(":\n");
93 sb.append(snippet);
94 }
95 if (problem != null) {
96
97
98 sb.append(": ").append(problem.replaceAll("\\s*\\t\\s*", " "));
99 }
100 } else {
101 sb.append(": ").append(ye.getMessage());
102 }
103
104 return sb.toString();
105 }
106
107 private static class CustomConstructor extends Constructor {
108 public CustomConstructor(YamlReader yamlReader, Resource baseResource) {
109 yamlConstructors.put(new Tag("!include"), new IncludeConstruct( yamlReader, baseResource));
110 }
111 }
112
113 private static class IncludeConstruct extends AbstractConstruct {
114 private final YamlReader yamlReader;
115 private final Resource baseResource;
116
117 public IncludeConstruct(YamlReader yamlReader, Resource baseResource) {
118 this.yamlReader = yamlReader;
119 this.baseResource = baseResource;
120 }
121
122 @Override
123 public Object construct(Node node) {
124 if (!(node instanceof ScalarNode)) {
125 throw new IllegalArgumentException("Non-scalar !include: " + node.toString());
126 }
127
128 final ScalarNode scalarNode = (ScalarNode) node;
129 final String path = scalarNode.getValue();
130
131 final Resource include = baseResource.getOrigin().getByPath(path);
132 try {
133 return yamlReader.readNoCast(include);
134 } catch (IOException e) {
135 throw new RuntimeException(e);
136 }
137 }
138 }
139 }