I looked at XStream and it looks more complicated than I need, and I'm not sure how it deals with attributes/elements not present in the java class (the reason I'm using a HashMap). Well the use of XML in this case is indeed irrelevant as I could just as well be building aliases for JSON or Google's Protocol Buffers, I'm just using XML for it. Though that does help resolve my conflicting ideas on how I want the aliases to be registered (the XStream part, not so much the the mentality thing - I considered multiple syntaxes/languages for it).
Perhaps I should have said html/android/fxml like XML rather than just XML (I do have my own styling language as well, similar to basic CSS)
And I have to be honest creating an automated parser for this isn't that hard, I'll post the code for how I'm parsing later if you're interested
Here you go (I don't think the code highlighter likes the angle brackets in my code, if you want it correctly you might need to copy it from a quote when writing a reply):
/*
* This program is free software. It comes without any warranty, to
* the extent permitted by applicable law. You can redistribute it
* and/or modify it under the terms of the Do What The f*ck You Want
* To Public License, Version 2, as published by Sam Hocevar. See
* http://www.wtfpl.net/ for more details.
*/
package com.bjd.main.util.ui;
import com.bjd.main.util.Node;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import javax.xml.stream.*;
import javax.xml.stream.events.Attribute;
import javax.xml.stream.events.StartElement;
import javax.xml.stream.events.XMLEvent;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.lang.reflect.InvocationTargetException;
import java.util.HashMap;
import java.util.Iterator;
/**
* @Author Benjamin Claassen
* @Version 1.0.0
*/
public class GuiLoader {
private static Logger logger = LoggerFactory.getLogger(GuiLoader.class);
private static final XMLInputFactory factory = XMLInputFactory.newInstance();
private static HashMap<String, Class> aliases = new HashMap<>();
/**
* Parses a valid XML file from stream
*
* @param stream The Stream that contains valid XML data
* @return The constructed GUI
* @throws XMLStreamException If the XML cannot be parsed
*/
public static Panel loadGUI(InputStream stream) throws XMLStreamException {
Panel result = new Panel();
Panel current = result;
XMLEventReader reader = factory.createXMLEventReader(new InputStreamReader(stream));
while (reader.hasNext()) {
XMLEvent event = reader.nextEvent();
switch (event.getEventType()) {
case XMLStreamConstants.START_ELEMENT:
StartElement element = event.asStartElement();
String name = element.getName().getLocalPart();
Class panel;
if (aliases.containsKey(name)) {
panel = aliases.get(name);
} else {
try {
panel = Class.forName(name);
} catch (ClassNotFoundException e) {
logger.error("Could not find class: {}\n{}", name, e);
return null;
}
}
if (panel == null) {
logger.error("Could not find class: {}");
return null;
}
Panel temp;
HashMap<String, String> properties = new HashMap<>();
Iterator attributes = element.getAttributes();
while (attributes.hasNext()) {
Attribute next = (Attribute) attributes.next();
properties.put(next.getName().getLocalPart(), next.getValue());
}
try {
temp = (Panel) panel.getConstructor(HashMap.class).newInstance(properties);
} catch (NoSuchMethodException e) {
try {
temp = (Panel) panel.newInstance();
} catch (InstantiationException e1) {
logger.error("Could not load GUI: {}\n{}", name, e);
return null;
} catch (IllegalAccessException e1) {
logger.error("\"Could not load GUI: {}\n{}", name, e);
return null;
}
} catch (IllegalAccessException e) {
logger.error("Could not load GUI: {}\n{}", name, e);
return null;
} catch (InstantiationException e) {
logger.error("Could not load GUI: {}\n{}", name, e);
return null;
} catch (InvocationTargetException e) {
logger.error("Could not load GUI: {}\n{}", name, e);
return null;
}
if (temp == null) {
logger.error("Could not load GUI: {}", name);
return null;
}
if (!current.addChild(temp)) {
logger.error("Could not add GUI: {}", name);
} else {
temp.setParent(current);
current = temp;
}
break;
case XMLStreamConstants.END_ELEMENT:
Node node = current.getParent();
if (node == null) {
break;
}
if (node instanceof Panel) {
current = (Panel) node;
} else {
logger.error("Parent of {} is illegal", current.getClass());
throw new IllegalStateException("Gui Element must extend Panel: "+ current.getClass());
}
}
}
return result;
}
/**
* Sets an alias for a GUI Element type, if type is null {@link #removeAlias(String)} is called instead
*
* @param name The name that is being aliased
* @param type The type the aliased belongs, this the type of GUI element created
* @param <T> To ensure only valid GUI Elements are added
*/
public static <T extends Panel> void setAlias(String name, Class<T> type) {
if (type == null) {
removeAlias(name);
} else {
aliases.put(name, type);
}
}
/**
* Removes an alias
*
* @param name The Alias to be removed
*/
public static void removeAlias(String name) {
aliases.remove(name);
}
}