uk.me.jeffsutton.pojogen.SimplePOJO.java Source code

Java tutorial

Introduction

Here is the source code for uk.me.jeffsutton.pojogen.SimplePOJO.java

Source

package uk.me.jeffsutton.pojogen;/*
                                 * Copyright (c) 2015 Jeff Sutton.
                                 *
                                 * Licensed under the Apache License, Version 2.0 (the "License");
                                 * you may not use this file except in compliance with the License.
                                 * You may obtain a copy of the License at
                                 *
                                 * http://www.apache.org/licenses/LICENSE-2.0
                                 *
                                 * Unless required by applicable law or agreed to in writing, software
                                 * distributed under the License is distributed on an "AS IS" BASIS,
                                 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
                                 * See the License for the specific language governing permissions and
                                 * limitations under the License.
                                 *
                                 */

import com.github.underscore.Function1;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import org.apache.commons.lang3.ArrayUtils;
import org.apache.xerces.jaxp.DocumentBuilderFactoryImpl;
import org.w3c.dom.Document;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import org.xml.sax.SAXException;

import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.transform.OutputKeys;
import javax.xml.transform.Source;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.stream.StreamResult;
import javax.xml.transform.stream.StreamSource;
import java.io.*;
import java.net.URL;
import java.nio.charset.StandardCharsets;
import java.text.NumberFormat;
import java.text.ParseException;
import java.util.*;

/**
 * <p>Class to parse XMl files and generate Java class models annotated for use with SimpleXML.</p>
 * <p>
 * Created by jeff on 03/12/2015.
 */
public class SimplePOJO {

    private static final String[] RESERVED_WORDS = { "default", "class", "public", "void" };
    private final String packageName;
    HashMap<String, XClass> classes = new HashMap<>();
    private String rootTageName;
    private int indentLevel = 0;

    public SimplePOJO(String packageName) {
        this.packageName = packageName;
    }

    public static void main(String[] argv) {
        String packageString = null;

        BufferedReader source = null;

        for (String a : argv) {
            if (a.startsWith("-p:")) {
                packageString = a.substring(3);
                System.out.println("Using package: " + packageString);
            }

            if (a.startsWith("-u:")) {
                try {
                    URL oracle = new URL(a.substring(3));
                    System.out.println("Using URL: " + oracle.toExternalForm());
                    source = new BufferedReader(new InputStreamReader(oracle.openStream(), StandardCharsets.UTF_8),
                            4096);
                } catch (Exception err) {
                    err.printStackTrace();
                }
            } else {
                source = new BufferedReader(new StringReader(""));
            }
        }

        if (source == null) {
            source = new BufferedReader(new StringReader(""));
        }

        System.out.println("\n\n");

        try {
            SimplePOJO simplePOJO = new SimplePOJO(packageString);
            String r = simplePOJO.generate(source);
            System.out.println(r);

            writeToFile(r, simplePOJO.rootTageName);

        } catch (Exception eek) {
            eek.printStackTrace();
        }

        //        try {
        //            Serializer serializer = new Persister();
        //            for (String a : argv) {
        //                if (a.startsWith("-u:")) {
        //                    try {
        //                        URL oracle = new URL(a.substring(3));
        //                        System.out.println("Using URL: " + oracle.toExternalForm());
        //                        source = new BufferedReader(
        //                                new InputStreamReader(oracle.openStream(), StandardCharsets.UTF_8), 4096);
        //                    } catch (Exception err) {
        //                        err.printStackTrace();
        //                    }
        //                } else {
        //                    source = new BufferedReader(new StringReader(""));
        //                }
        //            }
        //            Tv html = serializer.read(Tv.class, source, false);
        //            Gson gson = new GsonBuilder().setPrettyPrinting().serializeNulls().create();
        //            System.out.println("\n\n\n" + gson.toJson(html, Tv.class));
        //        } catch (Exception err) {
        //            err.printStackTrace();
        //        }
    }

    private static void writeToFile(String data, String rootTageName) {
        try {
            PrintWriter out = new PrintWriter(mkClassName(rootTageName) + ".java");
            out.println(data);
            out.close();
        } catch (Exception err) {
            err.printStackTrace();
        }
    }

    public static String reservedCheck(String str) {
        if (ArrayUtils.contains(RESERVED_WORDS, str)) {
            return "_" + str;
        }
        return str;
    }

    public static String mkClassName(String name) {
        name = getLastDotInList(name);
        name = name.replace("-", "_");
        name = name.replace("#", "");
        if (name.endsWith("_")) {
            name = name.substring(0, name.length() - 1);
        }
        while (name.contains("_")) {
            int pos = name.indexOf("_");
            name = name.substring(0, pos) + name.substring(pos + 1, pos + 2).toUpperCase()
                    + name.substring(pos + 2);
        }
        return reservedCheck(name);
    }

    public static String getLastDotInList(String str) {
        if (str.indexOf('.') > -1) {
            String[] sr = str.split("/`./");
            str = "";
            for (int i = 0; i < sr.length; i++) {
                sr[i] = cap(sr[i]);
                str += sr[i];
            }
        } else {
            str = cap(str);
        }
        return str;
    }

    public static String cap(String str) {
        return str.substring(0, 1).toUpperCase() + str.substring(1);
    }

    public static <String, XField> Collection<XField> values(final Map<String, XField> object) {
        return object.values();
    }

    public static <String, E> Map<String, List<Node>> groupBy(final NodeList iterable,
            final Function1<Node, String> func) {
        final Map<String, List<Node>> retVal = newLinkedHashMap();
        for (int i = 0; i < iterable.getLength(); i++) {
            Node e = iterable.item(i);
            final String key = func.apply(e);
            List<Node> val;
            if (retVal.containsKey(key)) {
                val = retVal.get(key);
            } else {
                val = newArrayList();
            }
            val.add(e);
            retVal.put(key, val);
        }
        return retVal;
    }

    @SuppressWarnings("unchecked")
    protected static <K, E> Map<K, E> newLinkedHashMap() {
        return new LinkedHashMap<>();
    }

    @SuppressWarnings("unchecked")
    protected static <T> List<T> newArrayList() {
        return new ArrayList<>();
    }

    public String getMainClassName() {
        return mkClassName(rootTageName);
    }

    public String pretyFormat(String src) {
        try {
            Source xmlInput = new StreamSource(new StringReader(src));
            StringWriter stringWriter = new StringWriter();
            StreamResult xmlOutput = new StreamResult(stringWriter);
            TransformerFactory transformerFactory = TransformerFactory.newInstance();

            Transformer transformer = transformerFactory.newTransformer();
            transformer.setOutputProperty(OutputKeys.INDENT, "yes");
            transformer.transform(xmlInput, xmlOutput);
            return xmlOutput.getWriter().toString();
        } catch (Exception e) {
            throw new RuntimeException(e); // simple exception handling, please review it
        }
    }

    public String generate(BufferedReader xml) throws ParserConfigurationException, SAXException, IOException {
        Document document = parse(xml);
        rootTageName = stripNS(document.getFirstChild().getNodeName());
        visitClass(document.getFirstChild());

        List<String> toRemove = new ArrayList<>();
        for (XClass xClass : classes.values()) {
            if (xClass.fields == null || xClass.fields.size() < 1) {
                toRemove.add(xClass.name);
            }
        }

        for (XClass xClass : classes.values()) {
            for (XField field : xClass.fields.values()) {
                if (toRemove.contains(field.dataType)) {
                    field.dataType = "String";
                }
            }
        }

        for (String s : toRemove) {
            classes.remove(s);
        }

        Gson gson = new GsonBuilder().setPrettyPrinting().create();
        System.out.println(gson.toJson(classes));
        return (generateClassText(classes.get(rootTageName)));
    }

    public Document parse(BufferedReader xml) throws IOException, SAXException, ParserConfigurationException {
        String file = "";
        try {
            String str;
            while ((str = xml.readLine()) != null) {
                file += str;
            }
        } catch (Exception e) {
            e.printStackTrace();
        }

        file = file.replaceAll("<!DOCTYPE((.|\n|\r)*?)\">", "");

        // convert String into InputStream
        InputStream is = new ByteArrayInputStream(file.getBytes());

        DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
        dbf.setFeature("http://apache.org/xml/features/nonvalidating/load-dtd-grammar", false);
        dbf.setFeature("http://apache.org/xml/features/nonvalidating/load-external-dtd", false);
        dbf.setFeature("http://apache.org/xml/features/disallow-doctype-decl", true);
        dbf.setFeature("http://xml.org/sax/features/validation", false);
        dbf.setNamespaceAware(false);
        dbf.setIgnoringComments(true);
        dbf.setValidating(false);
        dbf.setXIncludeAware(true);

        return dbf.newDocumentBuilder().parse(is);
    }

    public String stripNS(String str) {
        if (str.contains(":")) {
            return str.substring(str.indexOf(":") + 1);
        }
        return str;
    }

    public XClass visitClass(Node node) {
        String name = stripNS(node.getNodeName());
        System.out.println("Visiting class:\t" + name);
        if (!classes.containsKey(name)) {
            XClass cla = new XClass();
            cla.name = name;
            classes.put(name, cla);
            System.out.println("\tAdding class:\t" + name);
        }

        XClass cla = classes.get(name);
        System.out.println("\t\tChecking class:\t" + name + " :: " + node.getTextContent());
        if (node.getAttributes() != null && node.getAttributes().getLength() > 0) {
            System.out.println("\t\t\tReading attributes from class " + name);
            for (int i = 0; i < node.getAttributes().getLength(); i++) {
                System.out.println("Gerring attribute #" + i);
                try {
                    String akey = node.getAttributes().item(i).getNodeName();
                    if (akey.contains("xmlns:") || akey.equals("xmlns")) {
                        continue;
                    }
                    akey = stripNS(akey);

                    if (!cla.fields.containsKey(akey)) {
                        XField xf = new XField();
                        xf.name = akey;
                        xf.isInlineList = false;
                        xf.isAttribute = true;
                        System.out
                                .println("\t\t\tAdding attribute field: " + xf.name + " to " + cla.name.toString());
                        cla.fields.put(akey, xf);
                    }
                    XField xf = cla.fields.get(akey);
                    xf.dataType = getLiteralDataType(node.getAttributes().item(i).getNodeValue());
                    cla.fields.put(akey, xf);
                } catch (Exception err) {
                    err.printStackTrace();
                }
            }
        } else {
            System.out.println("Has no attributes");
        }

        Map<String, List<Node>> grouped = groupBy(node.getChildNodes(), new Function1<Node, String>() {

            @Override
            public String apply(Node arg) {
                return arg.getNodeName();
            }
        });

        for (Map.Entry<String, List<Node>> entry : grouped.entrySet()) {
            String key = stripNS(entry.getKey());

            System.out.println("\t\t\tLooking at child node: " + key + " of class " + name + ", children: "
                    + entry.getValue().size());
            List<Node> nodes = entry.getValue();
            if (key.equals("#text") && entry.getValue().size() > 1) {
                continue;
            }
            {

                if (!cla.fields.containsKey(key)) {
                    XField field = new XField();
                    field.name = key;
                    cla.fields.put(key, field);
                    System.out.println("Adding field " + cla.name + " -- " + key);
                }

                XField field = cla.fields.get(key);
                if (nodes.size() > 1) {
                    field.isInlineList = true;
                }

                for (Node node1 : nodes) {
                    if (isClass(node1)) {
                        {
                            field.dataType = key;
                            XClass nested = visitClass(node1);

                            if (nested.fields != null && nested.fields.size() == 1
                                    && values(nested.fields).toArray(new XField[1])[0].isInlineList) {
                                classes.remove(nested.name);
                                XField nestedField = values(nested.fields).toArray(new XField[1])[0];
                                field.dataType = nestedField.dataType;
                                field.isList = true;
                            }
                        }
                        if (node1.getAttributes() != null && node1.getAttributes().getLength() > 0) {
                            for (int i = 0; i < node1.getAttributes().getLength(); i++) {
                                System.out.println(
                                        "Getting attribute #" + i + " of " + node1.getAttributes().getLength());
                                try {
                                    Node n = node.getAttributes().item(i);
                                    if (n == null)
                                        continue;
                                    String akey = n.getNodeName();
                                    if (akey.contains("xmlns:") || akey.equals("xmlns")) {
                                        continue;
                                    }
                                    akey = stripNS(akey);

                                    if (!cla.fields.containsKey(akey)) {
                                        XField xf = new XField();
                                        xf.name = akey;
                                        xf.isInlineList = false;
                                        xf.isAttribute = true;
                                        cla.fields.put(akey, xf);
                                        System.out.println("\t\t\tAdding attribute field: " + xf.name + " to "
                                                + cla.name.toString());
                                    }
                                    XField xf = cla.fields.get(akey);
                                    xf.dataType = getLiteralDataType(node1.getAttributes().item(i).getNodeValue());
                                    cla.fields.put(akey, xf);
                                } catch (Exception err) {
                                    err.printStackTrace();
                                }
                            }
                        }

                    } else {
                        field.dataType = getLiteralDataType(node1.getNodeValue());
                    }
                }

                cla.fields.put(key, field);
            }
        }

        if (cla.fields == null || cla.fields.size() < 1) {
            System.out.println("Class " + cla.name + " has no fields");
        }

        return cla;
    }

    public String generateClassText(XClass cls) {

        String headers = "", root = "", isStatic = "", fields = "", accessors = "", inners = "";
        if (cls.name.equals(rootTageName)) {

            if (packageName != null) {
                headers = "package " + packageName + ";\n";
            }

            headers += "\nimport org.simpleframework.xml.Attribute;\n" + "import org.simpleframework.xml.Element;\n"
                    + "import org.simpleframework.xml.Text;\n" + "import org.simpleframework.xml.ElementList;\n"
                    + "import org.simpleframework.xml.Root;\n\n" + "import java.net.URL;\n"
                    + "import java.util.List;\n";

            root = "\n@Root(name=\"" + cls.name + "\")\n";

            for (Map.Entry<String, XClass> cl : classes.entrySet()) {
                if (!cl.getValue().name.equals(rootTageName)) {
                    inners += generateClassText(cl.getValue()) + "\n";
                }
            }
        } else {
            indentLevel++;
            root = "\n";
            isStatic = "static ";
        }

        fields = generateFieldText(cls.fields);
        accessors = generateAccessors(cls.fields);

        String indentText = "";

        for (int i = 0; i < indentLevel; i++) {
            indentText += "    ";
        }

        indentLevel--;
        return headers + root + indentText + "public " + isStatic + "class " + mkClassName(cls.name) + " {\n"
                + fields + "" + accessors + inners + "\n" + indentText + "}";
    }

    public String generateFieldText(HashMap<String, XField> fields) {
        String str = "";
        indentLevel++;
        for (Map.Entry<String, XField> field : fields.entrySet()) {
            XField f = field.getValue();

            if (f.name.equals("#text")) {
                f.name = "textValue";
                f.dataType = "String";
                f.isInlineList = false;
                f.isList = false;
            }

            String annotation = f.isAttribute ? "@Attribute(name=\"" + f.name + "\", required=false)"
                    : "@Element(name=\"" + f.name + "\", required=false)";
            boolean isClass = classes.containsKey(f.dataType);
            String dataType = isClass ? mkClassName(f.dataType) : f.dataType;

            if (f.isList || f.isInlineList) {
                dataType = "List<" + dataType + ">";

                annotation = "@ElementList(name=\"" + f.name + "\", required=false"
                        + (f.isInlineList ? ", entry=\"" + f.name + "\", inline=true)" : ")");

            }

            if (f.name.equals("textValue")) {
                annotation = "@Text(required=false)";
            }

            String indentText = "";

            for (int i = 0; i < indentLevel; i++) {
                indentText += "    ";
            }

            str += "\n" + indentText + annotation + "\n" + indentText + dataType + " " + mkFieldName(f.name)
                    + ";\n";
        }
        indentLevel--;
        return str;
    }

    public String generateAccessors(HashMap<String, XField> fields) {
        String str = "";
        indentLevel++;

        String indentText = "";

        for (int i = 0; i < indentLevel; i++) {
            indentText += "    ";
        }

        for (Map.Entry<String, XField> field : fields.entrySet()) {
            XField f = field.getValue();
            boolean isClass = classes.containsKey(f.dataType);
            String dataType = isClass ? mkClassName(f.dataType) : f.dataType;
            if (f.isList || f.isInlineList) {
                dataType = "List<" + dataType + ">";
            }

            str += "\n" + indentText + "public " + dataType + " get" + cap(mkFieldName(cap(f.name)))
                    + "() {return this." + mkFieldName(f.name) + ";}\n";
            str += indentText + "public void set" + cap(mkFieldName(cap(f.name))) + "(" + dataType
                    + " value) {this." + mkFieldName(f.name) + " = value;}\n";
        }
        indentLevel--;
        return str;
    }

    public String mkFieldName(String name) {
        name = getLastDotInList(name);
        name = name.replace("-", "_");
        name = name.replace("#", "");
        name = name.substring(0, 1).toLowerCase() + name.substring(1);
        if (name.endsWith("_")) {
            name = name.substring(0, name.length() - 1);
        }
        while (name.contains("_")) {
            int pos = name.indexOf("_");
            name = name.substring(0, pos) + name.substring(pos + 1, pos + 2).toUpperCase()
                    + name.substring(pos + 2);
        }
        return reservedCheck(name);
    }

    private String getLiteralDataType(String nodeValue) {
        if (nodeValue == null)
            return "String";
        try {
            DateType.getDate(nodeValue);
            return "Date";
        } catch (Exception ignored) {

        }
        try {
            new URL(nodeValue);
            return "URL";
        } catch (Exception ignored) {

        }
        if (nodeValue.equals("true") || nodeValue.equals("false")) {
            return "Boolean";
        } else if (isNumeric(nodeValue)) {
            if (nodeValue.contains(".")) {
                return "Double";
            } else {
                try {
                    //noinspection ResultOfMethodCallIgnored
                    Integer.parseInt(nodeValue);
                    return "Integer";
                } catch (NumberFormatException err) {
                    return "Long";
                }
            }
        }

        return "String";
    }

    public boolean isNumeric(String value) {
        if (value.contains(" ")) {
            return false;
        }
        try {
            NumberFormat.getInstance().parse(value);
            return true;
        } catch (ParseException e) {
            // Not a number.
            return false;
        }
    }

    public boolean isClass(Node node) {
        int childNodes = 0;
        int attributes = 0;
        boolean isTextOnly = false;

        if (node.getChildNodes() != null && node.getChildNodes().getLength() > 0) {
            childNodes = node.getChildNodes().getLength();
        }

        if (node.getAttributes() != null && node.getAttributes().getLength() > 0) {
            attributes = node.getAttributes().getLength();
        }

        if (node.getChildNodes() != null && node.getChildNodes().getLength() == 1
                && node.getChildNodes().item(0).getNodeName().equals("#text") && attributes == 0) {
            isTextOnly = true;
        }

        return !isTextOnly;
    }

}