package com.framsticks.parsers; import java.io.File; import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.InputStream; import java.util.LinkedList; import java.util.List; import javax.xml.parsers.DocumentBuilder; import javax.xml.parsers.DocumentBuilderFactory; import org.apache.logging.log4j.Logger; import org.apache.logging.log4j.LogManager; import org.w3c.dom.Document; import org.w3c.dom.Element; import org.w3c.dom.NamedNodeMap; import org.w3c.dom.Node; import org.w3c.dom.NodeList; import com.framsticks.params.Access; import com.framsticks.params.ParamFlags; import com.framsticks.params.PrimitiveParam; import com.framsticks.params.Registry; import com.framsticks.util.AutoAttacher; import com.framsticks.util.AutoBuilder; import com.framsticks.util.FramsticksException; import com.framsticks.util.lang.Strings; import static com.framsticks.params.ParamsUtil.getParam; public class XmlLoader { private static final Logger log = LogManager.getLogger(XmlLoader.class); protected Registry registry = new Registry(); /** * */ public XmlLoader() { registry.registerAndBuild(AutoInjector.class); } /** * @return the registry */ public Registry getRegistry() { return registry; } boolean useLowerCase = false; /** * @param useLowerCase the useLowerCase to set */ public void setUseLowerCase(boolean useLowerCase) { this.useLowerCase = useLowerCase; } public String mangleName(String name) { return useLowerCase ? name.toLowerCase() : name; } public String mangleAttribute(String name) { return useLowerCase ? name.toLowerCase() : Strings.uncapitalize(name); } public Object processElement(Element element, Class enclosingClass) { final String name = mangleName(element.getNodeName()); if (name.equals("import")) { String className = element.getAttribute("class"); try { registry.registerAndBuild(Class.forName(className)); return null; } catch (ClassNotFoundException e) { throw new FramsticksException().msg("failed to import class").arg("name", name).cause(e); } } if (name.equals("include")) { String fileName = element.getAttribute("file"); if (Strings.notEmpty(fileName)) { try { return load(new FileInputStream(new File(fileName)), enclosingClass); } catch (FileNotFoundException e) { throw new FramsticksException().msg("failed to include file").arg("file", fileName).cause(e); } } String resourceName = element.getAttribute("resource"); if (Strings.notEmpty(resourceName)) { Class javaClass = enclosingClass; String className = element.getAttribute("class"); if (Strings.notEmpty(className)) { try { javaClass = Class.forName(className); } catch (ClassNotFoundException e) { throw new FramsticksException().msg("failed to find class for resource loading").arg("class name", className).cause(e); } } return load(javaClass.getResourceAsStream(resourceName), enclosingClass); } throw new FramsticksException().msg("invalid node"); } Access access = registry.createAccess(name); Object object = access.createAccessee(); assert object != null; access.select(object); NamedNodeMap attributes = element.getAttributes(); for (int i = 0; i < attributes.getLength(); ++i) { Node attributeNode = attributes.item(i); PrimitiveParam param = getParam(access, mangleAttribute(attributeNode.getNodeName()), PrimitiveParam.class); if (param.hasFlag(ParamFlags.READONLY)) { throw new FramsticksException().msg("cannot configure readonly param").arg("param", param).arg("in", access); } access.set(param, attributeNode.getNodeValue()); } NodeList children = element.getChildNodes(); log.debug("found {} children in {}", children.getLength(), object); for (int i = 0; i < children.getLength(); ++i) { Node childNode = children.item(i); if (!(childNode instanceof Element)) { continue; } Object childObject = processElement((Element) childNode, object.getClass()); if (childObject == null) { continue; } List childrenObjects = new LinkedList<>(); if (childObject instanceof AutoBuilder) { childrenObjects.addAll(((AutoBuilder) childObject).autoFinish()); } else { childrenObjects.add(childObject); } for (Object child : childrenObjects) { if (child instanceof AutoAttacher) { ((AutoAttacher) child).attachTo(access.getSelected()); } else { access.tryAutoAppend(child); } } } log.debug("loaded {}", object); return object; } protected Object load(InputStream stream, Class enclosingClass) { try { DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance(); DocumentBuilder db = factory.newDocumentBuilder(); Document document = db.parse(stream); document.getDocumentElement().normalize(); Element element = document.getDocumentElement(); assert element != null; return processElement(element, enclosingClass); } catch (Exception e) { throw new FramsticksException().msg("failed to load").cause(e); } } public T load(Class type, InputStream stream) { registry.registerAndBuild(type); Object object = load(stream, type); if (type.isAssignableFrom(object.getClass())) { return type.cast(object); } throw new FramsticksException().msg("invalid type has been loaded"); } }