[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; |
---|
[102] | 25 | import com.framsticks.util.AutoAttacher; |
---|
[90] | 26 | import com.framsticks.util.AutoBuilder; |
---|
[88] | 27 | import com.framsticks.util.FramsticksException; |
---|
[99] | 28 | import com.framsticks.util.lang.Strings; |
---|
[105] | 29 | import static com.framsticks.params.ParamsUtil.getParam; |
---|
[88] | 30 | |
---|
[87] | 31 | public class XmlLoader { |
---|
[100] | 32 | private static final Logger log = LogManager.getLogger(XmlLoader.class); |
---|
[87] | 33 | |
---|
[88] | 34 | protected Registry registry = new Registry(); |
---|
| 35 | |
---|
| 36 | /** |
---|
| 37 | * |
---|
| 38 | */ |
---|
| 39 | public XmlLoader() { |
---|
[100] | 40 | registry.registerAndBuild(AutoInjector.class); |
---|
[88] | 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 | |
---|
[99] | 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 | |
---|
[100] | 67 | public Object processElement(Element element, Class<?> enclosingClass) { |
---|
[99] | 68 | final String name = mangleName(element.getNodeName()); |
---|
[88] | 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 | } |
---|
[100] | 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 | } |
---|
[88] | 98 | |
---|
[100] | 99 | return load(javaClass.getResourceAsStream(resourceName), enclosingClass); |
---|
| 100 | } |
---|
| 101 | throw new FramsticksException().msg("invalid <include/> node"); |
---|
| 102 | } |
---|
[88] | 103 | |
---|
[100] | 104 | Access access = registry.createAccess(name); |
---|
| 105 | |
---|
[88] | 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); |
---|
[105] | 113 | PrimitiveParam<?> param = getParam(access, mangleAttribute(attributeNode.getNodeName()), PrimitiveParam.class); |
---|
[101] | 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()); |
---|
[88] | 118 | } |
---|
| 119 | |
---|
| 120 | NodeList children = element.getChildNodes(); |
---|
[100] | 121 | log.debug("found {} children in {}", children.getLength(), object); |
---|
[88] | 122 | for (int i = 0; i < children.getLength(); ++i) { |
---|
| 123 | Node childNode = children.item(i); |
---|
| 124 | if (!(childNode instanceof Element)) { |
---|
| 125 | continue; |
---|
| 126 | } |
---|
[100] | 127 | Object childObject = processElement((Element) childNode, object.getClass()); |
---|
[88] | 128 | if (childObject == null) { |
---|
| 129 | continue; |
---|
| 130 | } |
---|
| 131 | |
---|
[90] | 132 | List<Object> childrenObjects = new LinkedList<>(); |
---|
| 133 | |
---|
| 134 | if (childObject instanceof AutoBuilder) { |
---|
| 135 | childrenObjects.addAll(((AutoBuilder) childObject).autoFinish()); |
---|
| 136 | } else { |
---|
| 137 | childrenObjects.add(childObject); |
---|
[88] | 138 | } |
---|
[90] | 139 | |
---|
| 140 | for (Object child : childrenObjects) { |
---|
[102] | 141 | if (child instanceof AutoAttacher) { |
---|
| 142 | ((AutoAttacher) child).attachTo(access.getSelected()); |
---|
| 143 | } else { |
---|
| 144 | access.tryAutoAppend(child); |
---|
| 145 | } |
---|
[90] | 146 | } |
---|
[88] | 147 | } |
---|
[100] | 148 | log.debug("loaded {}", object); |
---|
[88] | 149 | |
---|
| 150 | return object; |
---|
| 151 | } |
---|
| 152 | |
---|
[100] | 153 | protected Object load(InputStream stream, Class<?> enclosingClass) { |
---|
[88] | 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 | |
---|
[100] | 163 | return processElement(element, enclosingClass); |
---|
[88] | 164 | |
---|
| 165 | } catch (Exception e) { |
---|
| 166 | throw new FramsticksException().msg("failed to load").cause(e); |
---|
| 167 | } |
---|
| 168 | } |
---|
| 169 | |
---|
[90] | 170 | public <T> T load(Class<T> type, InputStream stream) { |
---|
[88] | 171 | registry.registerAndBuild(type); |
---|
| 172 | |
---|
[100] | 173 | Object object = load(stream, type); |
---|
[88] | 174 | if (type.isAssignableFrom(object.getClass())) { |
---|
| 175 | return type.cast(object); |
---|
| 176 | } |
---|
| 177 | throw new FramsticksException().msg("invalid type has been loaded"); |
---|
| 178 | } |
---|
[87] | 179 | } |
---|
[88] | 180 | |
---|