1 | package com.framsticks.parsers; |
---|
2 | |
---|
3 | import java.io.File; |
---|
4 | import java.io.FileInputStream; |
---|
5 | import java.io.FileNotFoundException; |
---|
6 | import java.io.InputStream; |
---|
7 | import java.util.LinkedList; |
---|
8 | import java.util.List; |
---|
9 | |
---|
10 | import javax.xml.parsers.DocumentBuilder; |
---|
11 | import javax.xml.parsers.DocumentBuilderFactory; |
---|
12 | |
---|
13 | import org.apache.logging.log4j.Logger; |
---|
14 | import org.apache.logging.log4j.LogManager; |
---|
15 | import org.w3c.dom.Document; |
---|
16 | import org.w3c.dom.Element; |
---|
17 | import org.w3c.dom.NamedNodeMap; |
---|
18 | import org.w3c.dom.Node; |
---|
19 | import org.w3c.dom.NodeList; |
---|
20 | |
---|
21 | import com.framsticks.params.Access; |
---|
22 | import com.framsticks.params.ParamFlags; |
---|
23 | import com.framsticks.params.PrimitiveParam; |
---|
24 | import com.framsticks.params.Registry; |
---|
25 | import com.framsticks.util.AutoAttacher; |
---|
26 | import com.framsticks.util.AutoBuilder; |
---|
27 | import com.framsticks.util.FramsticksException; |
---|
28 | import com.framsticks.util.lang.Strings; |
---|
29 | import static com.framsticks.params.ParamsUtil.getParam; |
---|
30 | |
---|
31 | public class XmlLoader { |
---|
32 | private static final Logger log = LogManager.getLogger(XmlLoader.class); |
---|
33 | |
---|
34 | protected Registry registry = new Registry(); |
---|
35 | |
---|
36 | /** |
---|
37 | * |
---|
38 | */ |
---|
39 | public XmlLoader() { |
---|
40 | registry.registerAndBuild(AutoInjector.class); |
---|
41 | } |
---|
42 | |
---|
43 | /** |
---|
44 | * @return the registry |
---|
45 | */ |
---|
46 | public Registry getRegistry() { |
---|
47 | return registry; |
---|
48 | } |
---|
49 | |
---|
50 | boolean useLowerCase = false; |
---|
51 | |
---|
52 | /** |
---|
53 | * @param useLowerCase the useLowerCase to set |
---|
54 | */ |
---|
55 | public void setUseLowerCase(boolean useLowerCase) { |
---|
56 | this.useLowerCase = useLowerCase; |
---|
57 | } |
---|
58 | |
---|
59 | public String mangleName(String name) { |
---|
60 | return useLowerCase ? name.toLowerCase() : name; |
---|
61 | } |
---|
62 | |
---|
63 | public String mangleAttribute(String name) { |
---|
64 | return useLowerCase ? name.toLowerCase() : Strings.uncapitalize(name); |
---|
65 | } |
---|
66 | |
---|
67 | public Object processElement(Element element, Class<?> enclosingClass) { |
---|
68 | final String name = mangleName(element.getNodeName()); |
---|
69 | if (name.equals("import")) { |
---|
70 | String className = element.getAttribute("class"); |
---|
71 | try { |
---|
72 | registry.registerAndBuild(Class.forName(className)); |
---|
73 | return null; |
---|
74 | } catch (ClassNotFoundException e) { |
---|
75 | throw new FramsticksException().msg("failed to import class").arg("name", name).cause(e); |
---|
76 | } |
---|
77 | } |
---|
78 | if (name.equals("include")) { |
---|
79 | String fileName = element.getAttribute("file"); |
---|
80 | if (Strings.notEmpty(fileName)) { |
---|
81 | try { |
---|
82 | return load(new FileInputStream(new File(fileName)), enclosingClass); |
---|
83 | } catch (FileNotFoundException e) { |
---|
84 | throw new FramsticksException().msg("failed to include file").arg("file", fileName).cause(e); |
---|
85 | } |
---|
86 | } |
---|
87 | String resourceName = element.getAttribute("resource"); |
---|
88 | if (Strings.notEmpty(resourceName)) { |
---|
89 | Class<?> javaClass = enclosingClass; |
---|
90 | String className = element.getAttribute("class"); |
---|
91 | if (Strings.notEmpty(className)) { |
---|
92 | try { |
---|
93 | javaClass = Class.forName(className); |
---|
94 | } catch (ClassNotFoundException e) { |
---|
95 | throw new FramsticksException().msg("failed to find class for resource loading").arg("class name", className).cause(e); |
---|
96 | } |
---|
97 | } |
---|
98 | |
---|
99 | return load(javaClass.getResourceAsStream(resourceName), enclosingClass); |
---|
100 | } |
---|
101 | throw new FramsticksException().msg("invalid <include/> node"); |
---|
102 | } |
---|
103 | |
---|
104 | Access access = registry.createAccess(name); |
---|
105 | |
---|
106 | Object object = access.createAccessee(); |
---|
107 | assert object != null; |
---|
108 | access.select(object); |
---|
109 | |
---|
110 | NamedNodeMap attributes = element.getAttributes(); |
---|
111 | for (int i = 0; i < attributes.getLength(); ++i) { |
---|
112 | Node attributeNode = attributes.item(i); |
---|
113 | PrimitiveParam<?> param = getParam(access, mangleAttribute(attributeNode.getNodeName()), PrimitiveParam.class); |
---|
114 | if (param.hasFlag(ParamFlags.READONLY)) { |
---|
115 | throw new FramsticksException().msg("cannot configure readonly param").arg("param", param).arg("in", access); |
---|
116 | } |
---|
117 | access.set(param, attributeNode.getNodeValue()); |
---|
118 | } |
---|
119 | |
---|
120 | NodeList children = element.getChildNodes(); |
---|
121 | log.debug("found {} children in {}", children.getLength(), object); |
---|
122 | for (int i = 0; i < children.getLength(); ++i) { |
---|
123 | Node childNode = children.item(i); |
---|
124 | if (!(childNode instanceof Element)) { |
---|
125 | continue; |
---|
126 | } |
---|
127 | Object childObject = processElement((Element) childNode, object.getClass()); |
---|
128 | if (childObject == null) { |
---|
129 | continue; |
---|
130 | } |
---|
131 | |
---|
132 | List<Object> childrenObjects = new LinkedList<>(); |
---|
133 | |
---|
134 | if (childObject instanceof AutoBuilder) { |
---|
135 | childrenObjects.addAll(((AutoBuilder) childObject).autoFinish()); |
---|
136 | } else { |
---|
137 | childrenObjects.add(childObject); |
---|
138 | } |
---|
139 | |
---|
140 | for (Object child : childrenObjects) { |
---|
141 | if (child instanceof AutoAttacher) { |
---|
142 | ((AutoAttacher) child).attachTo(access.getSelected()); |
---|
143 | } else { |
---|
144 | access.tryAutoAppend(child); |
---|
145 | } |
---|
146 | } |
---|
147 | } |
---|
148 | log.debug("loaded {}", object); |
---|
149 | |
---|
150 | return object; |
---|
151 | } |
---|
152 | |
---|
153 | protected Object load(InputStream stream, Class<?> enclosingClass) { |
---|
154 | try { |
---|
155 | DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance(); |
---|
156 | DocumentBuilder db = factory.newDocumentBuilder(); |
---|
157 | |
---|
158 | Document document = db.parse(stream); |
---|
159 | document.getDocumentElement().normalize(); |
---|
160 | Element element = document.getDocumentElement(); |
---|
161 | assert element != null; |
---|
162 | |
---|
163 | return processElement(element, enclosingClass); |
---|
164 | |
---|
165 | } catch (Exception e) { |
---|
166 | throw new FramsticksException().msg("failed to load").cause(e); |
---|
167 | } |
---|
168 | } |
---|
169 | |
---|
170 | public <T> T load(Class<T> type, InputStream stream) { |
---|
171 | registry.registerAndBuild(type); |
---|
172 | |
---|
173 | Object object = load(stream, type); |
---|
174 | if (type.isAssignableFrom(object.getClass())) { |
---|
175 | return type.cast(object); |
---|
176 | } |
---|
177 | throw new FramsticksException().msg("invalid type has been loaded"); |
---|
178 | } |
---|
179 | } |
---|
180 | |
---|