dk.nsi.stamdata.replication.tools.SchemaGenerator.java Source code

Java tutorial

Introduction

Here is the source code for dk.nsi.stamdata.replication.tools.SchemaGenerator.java

Source

/**
 * The contents of this file are subject to the Mozilla Public
 * License Version 1.1 (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.mozilla.org/MPL/
 *
 * Software distributed under the License is distributed on an "AS
 * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
 * implied. See the License for the specific language governing
 * rights and limitations under the License.
 *
 * Contributor(s): Contributors are attributed in the source code
 * where applicable.
 *
 * The Original Code is "Stamdata".
 *
 * The Initial Developer of the Original Code is Trifork Public A/S.
 *
 * Portions created for the Original Code are Copyright 2011,
 * Lgemiddelstyrelsen. All Rights Reserved.
 *
 * Portions created for the FMKi Project are Copyright 2011,
 * National Board of e-Health (NSI). All Rights Reserved.
 */
package dk.nsi.stamdata.replication.tools;

import com.google.inject.Guice;
import com.google.inject.Key;
import com.google.inject.TypeLiteral;
import com.trifork.stamdata.Namespace;
import com.trifork.stamdata.Nullable;
import com.trifork.stamdata.persistence.RecordSpecification;
import com.trifork.stamdata.specs.BemyndigelseRecordSpecs;
import com.trifork.stamdata.specs.SikredeRecordSpecs;
import com.trifork.stamdata.specs.YderregisterRecordSpecs;
import dk.nsi.stamdata.views.View;
import dk.nsi.stamdata.views.ViewModule;
import org.dom4j.Document;
import org.dom4j.DocumentFactory;
import org.dom4j.Element;
import org.dom4j.io.OutputFormat;
import org.dom4j.io.XMLWriter;

import javax.xml.bind.annotation.XmlSchema;
import javax.xml.bind.annotation.XmlTransient;
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.io.Writer;
import java.lang.reflect.Field;
import java.math.BigInteger;
import java.util.Collection;
import java.util.Date;
import java.util.Map;

import static com.trifork.stamdata.Preconditions.checkArgument;

public class SchemaGenerator {
    private static final Key<Map<String, Class<? extends View>>> view = Key
            .get(new TypeLiteral<Map<String, Class<? extends View>>>() {
            });

    public static void main(String[] args) throws IOException {
        File outputDir = new File(args[0]);

        Collection<Class<? extends View>> views = Guice.createInjector(new ViewModule()).getInstance(view).values();

        for (Class<? extends View> view : views) {
            String packageName = view.getPackage().getName();
            String subDirName = packageName.substring(packageName.lastIndexOf(".") + 1);
            String viewName = view.getSimpleName().toLowerCase();

            Writer writer = createWriterFor(outputDir, subDirName, viewName);

            generate(view, writer);

            writer.flush();
            writer.close();
        }

        // HACK: Make generic for all record specs.

        generateRecordXsd(outputDir, "sikrede", "sikrede", SikredeRecordSpecs.ENTRY_RECORD_SPEC);
        generateRecordXsd(outputDir, "yderregisteret", "yder", YderregisterRecordSpecs.YDER_RECORD_TYPE);
        generateRecordXsd(outputDir, "yderregisteret", "person", YderregisterRecordSpecs.PERSON_RECORD_TYPE);
    }

    private static void generateRecordXsd(File outputDir, String register, String entityName,
            RecordSpecification spec) throws IOException {
        Writer writer = createWriterFor(outputDir, register, entityName);

        generate(spec, writer, register);

        writer.flush();
        writer.close();
    }

    private static FileWriter createWriterFor(File outputDir, String register, String entityName)
            throws IOException {
        File registerDir = new File(outputDir, register);
        registerDir.mkdirs();

        File schemaFile = new File(registerDir, entityName + ".xsd");
        return new FileWriter(schemaFile);
    }

    public static void generate(Class<? extends View> entity, Writer writer) throws IOException {
        Document doc = DocumentFactory.getInstance().createDocument();

        String targetNamespace = entity.getPackage().getAnnotation(XmlSchema.class).namespace();
        String entityName = entity.getSimpleName().toLowerCase();

        Element all = generate(doc, targetNamespace, entityName);

        for (Field method : entity.getDeclaredFields()) {
            if (method.isAnnotationPresent(XmlTransient.class))
                continue;

            String name = method.getName();
            String type = convert2SchemaType(method);
            addElement(all, name, type, null);
        }

        XMLWriter xmlWriter = new XMLWriter(writer, OutputFormat.createPrettyPrint());
        xmlWriter.write(doc);
    }

    private static Element generate(Document doc, String namespace, String entityName) {
        Element root = doc.addElement("xs:schema");

        root.addNamespace("xs", "http://www.w3.org/2001/XMLSchema");
        root.addNamespace("tns", namespace);
        root.addAttribute("targetNamespace", namespace);

        Element element = root.addElement("xs:element");
        element.addAttribute("name", entityName);

        Element complexType = element.addElement("xs:complexType");

        // We use "all" and not "sequence" we cause we cannot tell from the
        // class the order of the elements.
        //
        return complexType.addElement("xs:all");
    }

    private static void addElement(Element parent, String name, String type, @Nullable Integer length) {
        Element field = parent.addElement("xs:element");
        field.addAttribute("name", name);

        if (length != null && type.equals("xs:string")) {
            field.addElement("xs:simpleType").addElement("xs:restriction").addElement("xs:maxLength")
                    .addAttribute("value", length.toString());
        } else {
            field.addAttribute("type", type);
        }
    }

    private static String convert2SchemaType(Field field) {
        if (String.class.isAssignableFrom(field.getType())) {
            return "xs:string";
        } else if (Long.class.isAssignableFrom(field.getType())
                || BigInteger.class.isAssignableFrom(field.getType())
                || long.class.isAssignableFrom(field.getType()) || int.class.isAssignableFrom(field.getType())) {
            return "xs:integer";
        } else if (Date.class.isAssignableFrom(field.getType())) {
            return "xs:dateTime";
        } else if (Float.class.isAssignableFrom(field.getType())) {
            return "xs:float";
        } else if (Double.class.isAssignableFrom(field.getType())) {
            return "xs:double";
        } else if (Boolean.class.isAssignableFrom(field.getType())
                || boolean.class.isAssignableFrom(field.getType())) {
            return "xs:boolean";
        } else {
            checkArgument(false, "Return type '%s' on field %s is not supported.", field.getType(),
                    field.toString());
            return null;
        }
    }

    public static void generate(RecordSpecification specification, Writer writer, String register)
            throws IOException {
        Document doc = DocumentFactory.getInstance().createDocument();

        // FIXME: Register added to record spec.

        String namespace = Namespace.STAMDATA_3_0 + "/" + register;
        String entityName = specification.getTable().toLowerCase();

        Element all = generate(doc, namespace, entityName);

        for (RecordSpecification.FieldSpecification field : specification.getFieldSpecs()) {
            addElement(all, field.name, convert2XsdType(field.type), field.length);
        }

        all.addElement("xs:element").addAttribute("name", "validFrom").addAttribute("type", "xs:dateTime");
        all.addElement("xs:element").addAttribute("name", "validTo").addAttribute("type", "xs:dateTime");

        XMLWriter xmlWriter = new XMLWriter(writer, OutputFormat.createPrettyPrint());
        xmlWriter.write(doc);
    }

    private static String convert2XsdType(RecordSpecification.RecordFieldType fieldType) {
        if (fieldType == RecordSpecification.RecordFieldType.ALPHANUMERICAL) {
            return "xs:string";
        } else if (fieldType == RecordSpecification.RecordFieldType.NUMERICAL) {
            return "xs:integer";
        } else {
            throw new AssertionError();
        }
    }
}