Symlink
Symlink

Reputation: 464

Is it possible to create an enum with parameters using an .txt or .xml file?

I would like to create an enum class using a file. I hope to make maintenance easier.

txt example:

//name of the enum instance and devided by a '-' are the parameter values:

JOHN-23
ANNA-19

xml example:

<friends>
    <friend name="JOHN">
        <age>23</age>
    </friend>
    <friend name="ANNA">
        <age>19</age>
    </friend>
</friends>

I would like to have an enum akting like this one:

enum Friends {
    JOHN(23),
    ANNA(19);

    private int age;

    Friends(int age) {
        this.age = age;
    }
}

Upvotes: 0

Views: 431

Answers (2)

Johannes Kuhn
Johannes Kuhn

Reputation: 15173

Here an example how you can generate the enum with StAX in java:

package codegen;

import java.io.FileWriter;
import java.io.IOException;
import java.net.URL;

import javax.xml.stream.XMLInputFactory;
import javax.xml.stream.XMLStreamException;
import javax.xml.stream.XMLStreamReader;

public class GenCode {
    public static void main(String[] args) throws XMLStreamException, IOException {
        URL xmlFile = GenCode.class.getResource("Friends.xml");
        XMLInputFactory inFactory = XMLInputFactory.newFactory();
        XMLStreamReader reader = inFactory.createXMLStreamReader(xmlFile.openStream());

        try (FileWriter out = new FileWriter("generated/codegen/Friends.java")) {
            out.write("package codegen;\n");
            out.write("\n");
            out.write("public enum Friends {\n");

            String friendName = null;
            boolean inAge = false;
            String sep = "\t";
            while (reader.hasNext()) {
                switch (reader.next()) {
                    case XMLStreamReader.START_ELEMENT:
                        if (reader.getLocalName().equals("friend"))
                            friendName = reader.getAttributeValue(null, "name");
                        if (reader.getLocalName().equals("age"))
                            inAge = true;
                        break;
                    case XMLStreamReader.CHARACTERS:
                        if (inAge) {
                            out.write(sep + friendName + "_" + reader.getText());
                            sep = ",\n\t";
                        }
                        break;
                    case XMLStreamReader.END_ELEMENT:
                        if (reader.getLocalName().equals("age"))
                            inAge = false;
                        break;
                }

            }
            out.write("\n}");
        }
    }
}

You might need to change some paths.

You have to compile this file, invoke it with java, which will create the Friends.java with the enum and then compile the rest.

Upvotes: 0

Pete Kirkham
Pete Kirkham

Reputation: 49331

You can do it with an XSLT transformation and call out to SAXON via a task in your build system.

e.g. applying this to your example XML will result in your example enum code

<xsl:stylesheet
  xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
  version="2.0">
  <xsl:output method="text" indent="no"/>

  <xsl:variable name="classname"><xsl:sequence select="concat(upper-case(substring(/*/local-name(),1,1)), substring(/*/local-name(), 2), ' '[not(last())])"/>  </xsl:variable>

  <xsl:template match="/*">
    enum <xsl:value-of select="$classname"/>
    {<xsl:for-each select="*"><xsl:if test="position()!=1">,</xsl:if><xsl:text>
        </xsl:text><xsl:value-of select="@name"/>(<xsl:for-each select="*"><xsl:if test="position()!=1">, </xsl:if><xsl:value-of select="text()"/></xsl:for-each>)</xsl:for-each>;

<xsl:for-each select="*[1]/*">        private int <xsl:value-of select="local-name()"/>;
</xsl:for-each><xsl:text> 
        </xsl:text><xsl:value-of select="$classname"/>(<xsl:for-each select="*[1]/*"><xsl:if test="position()!=1">, </xsl:if>int <xsl:value-of select="local-name()"/></xsl:for-each>)
        {
<xsl:for-each select="*[1]/*">            this.<xsl:value-of select="local-name()"/> = <xsl:value-of select="local-name()"/>;
</xsl:for-each>        }
    }
  </xsl:template>
</xsl:stylesheet>

However,

  • it would break if the XML didn't have the same number of parameters for each enum value.
  • your input is encoding type names and field names as element names, whereas it's easier for metamodels to encode them as attributes
  • it's easier to write transforms for explicit rather than implicit information (i.e. say that you have an int age parameter rather than just happening to have age elements whose content is a string of decimal digits)
  • if you move on to anything a bit more complicated, such as generating hierarchies of classes, the queries to resolve overloads and inheritance rapidly go past simple XSLT

Upvotes: 1

Related Questions