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