All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.enhydra.xml.xmlc.compiler.ClassGenerator Maven / Gradle / Ivy

The newest version!
/*
 * Enhydra Java Application Server Project
 * 
 * The contents of this file are subject to the Enhydra 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 on
 * the Enhydra web site ( http://www.enhydra.org/ ).
 * 
 * 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 terms governing rights and limitations
 * under the License.
 * 
 * The Initial Developer of the Enhydra Application Server is Lutris
 * Technologies, Inc. The Enhydra Application Server and portions created
 * by Lutris Technologies, Inc. are Copyright Lutris Technologies, Inc.
 * All Rights Reserved.
 * 
 * Contributor(s):
 * 
 * $Id: ClassGenerator.java,v 1.2 2005/01/26 08:29:24 jkjome Exp $
 */

package org.enhydra.xml.xmlc.compiler;

import java.io.File;
import java.io.IOException;
import java.io.PrintWriter;

import org.enhydra.xml.xmlc.XMLCException;
import org.enhydra.xml.xmlc.XMLObject;
import org.enhydra.xml.xmlc.codegen.IndentWriter;
import org.enhydra.xml.xmlc.codegen.JavaClass;
import org.enhydra.xml.xmlc.codegen.JavaCode;
import org.enhydra.xml.xmlc.codegen.JavaField;
import org.enhydra.xml.xmlc.codegen.JavaLang;
import org.enhydra.xml.xmlc.codegen.JavaMethod;
import org.enhydra.xml.xmlc.codegen.JavaModifiers;
import org.enhydra.xml.xmlc.codegen.JavaParameter;
import org.enhydra.xml.xmlc.deferredparsing.DeferredParsingAccessorGenerator;
import org.enhydra.xml.xmlc.deferredparsing.DeferredParsingDocBuilderGenerator;
import org.enhydra.xml.xmlc.dom.AccessorGenerator;
import org.enhydra.xml.xmlc.dom.DocBuilderGenerator;
import org.enhydra.xml.xmlc.dom.XMLCDocument;
import org.enhydra.xml.xmlc.dom.XMLCDomFactory;
import org.enhydra.xml.xmlc.dom.XMLCDomFactoryCache;
import org.enhydra.xml.xmlc.metadata.DocumentClass;
import org.enhydra.xml.xmlc.metadata.GenerateType;
import org.enhydra.xml.xmlc.metadata.MetaData;
import org.w3c.dom.Document;

/**
 * Generate XML classes and interfaces.
 */
public class ClassGenerator {
    /*
     * List of standard imports for generated classes and interfaces.
     */
    private static final String[] IMPORT_LIST = {
        "org.w3c.dom.*",
    };

    /*
     * List of standard imports for generated classes.
     */
    private static final String[] CLASS_IMPORT_LIST = {
//        "org.enhydra.xml.xmlc.XMLCUtil",
//        "org.enhydra.xml.xmlc.XMLCError",
        "org.enhydra.xml.xmlc.dom.XMLCDomFactory",
//        "org.enhydra.xml.dom.DOMOps"
    };

    /*
     * List of additional imports for deferred parsing support.
     */
    private static final String[] DEFERRED_PARSING_CLASS_IMPORT_LIST = {
        "org.enhydra.xml.xmlc.deferredparsing.DocumentLoader",
        "org.enhydra.xml.xmlc.deferredparsing.StandardDocumentLoader",
    };

    /**
     * Name of field containing document loader reference for deferred parsing.
     */
    public static final String DOC_LOADER_FIELD_NAME = "fDocumentLoader";

    /**
     * Class name of the document loader for deferred parsing.
     */
    public static final String DOC_LOADER_CLASS_NAME = "DocumentLoader";

    /**
     * Name of field containing delegate reference.
     */
    public static final String DELEGATE_FIELD_NAME = "fDelegate";

    /** 
     * Name for the static field holding the XMLCDomFactory.
     */
    public static final String DOM_FACTORY_FIELD_NAME = "fDOMFactory";

    /*
     * Header to place in each generated file.
     */
    private static final String[] WARNING_HEADER = {
        "/*",
        " ************************************",
        " * XMLC GENERATED CODE, DO NOT EDIT *",
        " ************************************",
        " */"
    };

    /**
     * Document metadata.
     */
    private MetaData fMetaData;
    private DocumentClass fDocumentClass;

    /*
     * What to generate.
     */
    private GenerateType fGenerate;

    /*
     * XMLC DOM management object.
     */
    private XMLCDocument fXmlcDoc;

    /*
     * Source document.
     */
    private Document fDocument;

    /**
     * Table of elements used by code generators.
     */
    private ElementTable fElementTable;

    /*
     * Access method creator.
     */
    private AccessMethods fAccessMethods;

    /*
     * Access constants creator.
     */
    private AccessConsts fAccessConsts;

    /**
     * DOM Accessor generator.
     */
    private AccessorGenerator fAccessorGenerator;

    /*
     * Document builder generator.
     */
    private DocBuilderGenerator fDocBuilderGenerator;

    /**
     * Class being constructed.
     */
    JavaClass fDocClass;

    /**
     * Construct an object.
     *
     * @param metaData Document metadata.
     * @param xmlcDoc XMLC DOM object containing the document.
     * @param methodOutput Write information about the generated 
     *  methods to this file if not NULL.
     * @exception XMLCException If an error is detected.
     */
    public ClassGenerator(MetaData metaData,
                          XMLCDocument xmlcDoc,
                          PrintWriter methodOutput)
            throws XMLCException {
        // Setup fields
        fMetaData = metaData;
        fXmlcDoc = xmlcDoc;
        fDocumentClass = fMetaData.getDocumentClass();
        fGenerate = fDocumentClass.getGenerate();
        fDocument = xmlcDoc.getDocument();

        // Get the deferred parsing specific generators if deferred
        // parsing support is desired. Otherwise, get the DOM specific
        // generators
	if (fDocumentClass.getDeferredParsing()) {
	    fAccessorGenerator =
		new DeferredParsingAccessorGenerator();
	    fDocBuilderGenerator =
		new DeferredParsingDocBuilderGenerator();
	} else {
	    XMLCDomFactory domFactory = fXmlcDoc.getDomFactory();
	    fAccessorGenerator = domFactory.createAccessorGenerator(fDocument);
	    fDocBuilderGenerator = domFactory.createDocBuilderGenerator(fDocument);
	}

        fElementTable = new ElementTable(metaData, fXmlcDoc);
        fAccessMethods = new AccessMethods(fMetaData, fElementTable,
                                           fAccessorGenerator);
        fAccessConsts = new AccessConsts(fMetaData, fElementTable);

        // Build class and imports
        createClass();
        fAccessMethods.generateCode(fDocClass);
        fAccessConsts.generateCode(fDocClass);

        // Construct the rest of the class.
        createConstructors();
	createDomFactoryField();
	if (fDocumentClass.getDeferredParsing()) {
	    createDeferredParsingFields();
	}
	
        createBuildDocument();
        fAccessMethods.generateCode(fDocClass);

        if (fDocumentClass.getDelegateSupport()) {
            createDelegateSupport();
        }
        createClassIdent();
        createSourceFileConst();

        // Output information about created and omitted accessors.
        if (methodOutput != null) {
            fAccessMethods.printAccessMethods(methodOutput);
            fAccessConsts.printAccessConstants(methodOutput);
            fAccessMethods.printOmittedIds(methodOutput);
            fAccessConsts.printOmittedConstants(methodOutput);
        }
    }

    /**
     * Create the default constructor (no arguments).
     */
    private void createDefaultConstructor() {
        JavaMethod constr
            = new JavaMethod(fDocumentClass.getUnqualClassName(),
                             null,
                             JavaModifiers.PUBLIC,
                             null,
                             new String[] {"Default constructor."});
        if (fDocumentClass.getDeferredParsing()) {
	    constr.getCode().addln("this(StandardDocumentLoader.getInstance());");
        }
        constr.getCode().addln("buildDocument();");
        fDocClass.addConstructor(constr);
    }

    /**
     * Create the buildDocument optional constructor.
     */
    private void createOptionalBuildDocConstructor() {
        String[] buildArgDoc = {
            "buildDOM If false, the DOM will not be built until",
            "buildDocument() is called by the derived class.  If true, ",
            "the DOM is built immediatly."
        };
        JavaParameter buildArg
            = new JavaParameter("buildDOM", "boolean", buildArgDoc);

        JavaMethod constr
            = new JavaMethod(fDocumentClass.getUnqualClassName(),
                             null,
                             JavaModifiers.PUBLIC, 
                             new JavaParameter[] {buildArg},
                             new String[] {"Constructor with optional building of the DOM."});
        if (fDocumentClass.getDeferredParsing()) {
	    constr.getCode().addln("this(StandardDocumentLoader.getInstance(), buildDOM);");
        } else constr.getCode().addln(new String[] {
            "if (buildDOM) {",
            "    buildDocument();",
            "}"
        });
        fDocClass.addConstructor(constr);
    }

    /**
     * Create the deferred parsing constructor
     */
    private void createDeferredParsingConstructors() {
        String[] buildArgDoc = {
            "buildDOM If false, the DOM will not be built until",
            "buildDocument() is called by the derived class.  If true, ",
            "the DOM is built immediatly."
        };
        JavaParameter buildArg
            = new JavaParameter("buildDOM", "boolean", buildArgDoc);

        String[] loaderArgDoc = {
            "loader the document loader to delegate document",
	    "creation to."
        };
        JavaParameter loaderArg
            = new JavaParameter("loader", "DocumentLoader", buildArgDoc);

        JavaMethod constr
            = new JavaMethod(fDocumentClass.getUnqualClassName(),
                             null,
                             JavaModifiers.PUBLIC, 
                             new JavaParameter[] {loaderArg, buildArg},
                             new String[] {
			        "Constructor for deferred parsing support.",
				"The supplied document loader is used to",
				"create the DOM."
			     });
        constr.getCode().addln(new String[] {
	    DOC_LOADER_FIELD_NAME + " = loader;",
            "if (buildDOM) {",
            "    buildDocument();",
            "}"
        });
        fDocClass.addConstructor(constr);

        constr
            = new JavaMethod(fDocumentClass.getUnqualClassName(),
                             null,
                             JavaModifiers.PUBLIC, 
                             new JavaParameter[] {loaderArg},
                             new String[] {
			        "Constructor for deferred parsing support.",
				"The supplied document loader is used to",
				"create the DOM."
			     });
        constr.getCode().addln("this(loader, true);");
        fDocClass.addConstructor(constr);
    }

    /**
     * Create the copy constructor.
     */
    private void createCopyConstructor() {
        //FIXME: Should really use interface name if one is generated.
        JavaParameter srcArg
            = new JavaParameter("src",
                                fDocumentClass.getUnqualClassName(),
                                "The document to clone.");

        JavaMethod constr
            = new JavaMethod(fDocumentClass.getUnqualClassName(),
                             null,
                             JavaModifiers.PUBLIC, 
                             new JavaParameter[] {srcArg},
                             new String[] {"Copy constructor."});
        if (fDocumentClass.getDeferredParsing()) {
	    constr.getCode().addln(DOC_LOADER_FIELD_NAME +
				   " = src.getDocumentLoader();");
        }
        constr.getCode().addln(new String[] {
            "setDocument((Document)src.getDocument().cloneNode(true), src.getMIMEType(), src.getEncoding());",
            "syncAccessMethods();"
        });
        fDocClass.addConstructor(constr);
    }

    /**
     * Create the clone method.
     */
    private void createCloneMethod() {
        JavaParameter deepArg
            = new JavaParameter("deep", "boolean",
                                "Must be true, only deep clone is supported.");

        JavaMethod method
            = new JavaMethod("cloneNode",
                             "Node",
                             JavaModifiers.PUBLIC | JavaModifiers.OMIT_INTERFACE,
                             new JavaParameter[] {deepArg},
                             new String[] {"Clone the document."});
        fDocClass.addMethod(method);
        method.getCode().addln(new String[] {
            "cloneDeepCheck(deep);",
            "return new " + fDocumentClass.getUnqualClassName() + "(this);"
        });
    }

    /**
     * Create the constructors and clone method
     */
    private void createConstructors() {
        createDefaultConstructor();
        createOptionalBuildDocConstructor();
        createCopyConstructor();
	if (fDocumentClass.getDeferredParsing()) {
	    createDeferredParsingConstructors();
	}
        createCloneMethod();
    }

    /**
     * Create the delegate support methods and fields.  Only
     * used when delegation support is being compiled in.
     */
    private void createDelegateSupport() {
        String unqualInterfaceName = fDocumentClass.getUnqualInterfaceName();

        // NOTE: the argument to setDelegate is not the specific interface
        // type since we want it to override the method in the base class.
        // The generated type-cast will catch the wrong type being passed in.
        JavaParameter param
            = new JavaParameter("delegate", XMLObject.class.getName(),
                                new String[] {
                                    "The delegate to set.  This must be an object",
                                    "implementing " + unqualInterfaceName + ", however the parameter",
                                    "is untyped, since this method must override XMLOject.setDelegate()",
                                    "New value for text child."});

        JavaMethod method
            = new JavaMethod("setDelegate",
                             "void",
                             JavaModifiers.PUBLIC,
                             new JavaParameter[] {param},
                             new String[] {"Set the delegate object."});
        fDocClass.addMethod(method);
        JavaCode body = method.getCode();

        body.addln(DELEGATE_FIELD_NAME + " = (" + unqualInterfaceName + ")delegate;");
        body.addln("super.setDelegate(" + DELEGATE_FIELD_NAME + ");");

        JavaField field = 
            new JavaField(DELEGATE_FIELD_NAME,
                          unqualInterfaceName,
                          JavaModifiers.PRIVATE,
                          "Pointer to delegate object", null);
        fDocClass.addField(field);
    }

    /**
     * Create the field containing the document loader and matching
     * access method. Only created if deferred parsing support is enabled.
     */
    private void createDeferredParsingFields() {
        String[] doc = {
            "Document loader for deferred parsing.",
        };
        JavaField field = 
            new JavaField(DOC_LOADER_FIELD_NAME,
                          DOC_LOADER_CLASS_NAME,
                          JavaModifiers.PRIVATE | JavaModifiers.FINAL,
                          doc, null);
        fDocClass.addField(field);

        JavaMethod method
            = new JavaMethod("getDocumentLoader",
                             DOC_LOADER_CLASS_NAME,
                             JavaModifiers.PROTECTED | JavaModifiers.FINAL,
                             null,
                             new String[] {"Get the document loader associated with the class."});
        fDocClass.addMethod(method);
        JavaCode body = method.getCode();
        body.addln("return " + DOC_LOADER_FIELD_NAME + ";");
    }

    /**
     * Create the static field that identifies the generated class.
     * Used by auto-recompilation to determine the XMLC generated
     * class in an inheritance hierarchy.
     */
    private void createClassIdent() {
        String[] doc = {
            "Field that is used to identify this as the XMLC generated class",
            "in an inheritance chain. Contains a reference to the class object."
        };
        //FIXME: Should this be in interface if both generated???
        JavaField field = 
            new JavaField(XMLObject.XMLC_GENERATED_CLASS_FIELD_NAME,
                          "Class",
                          JavaModifiers.PUBLIC_CONST | JavaModifiers.OMIT_INTERFACE,
                          doc,
                          fDocumentClass.getUnqualClassName() + ".class");
        fDocClass.addField(field);
    }

    /**
     * Create the static field that contains the name of the source file. Used
     * by auto-recompilation to find the file to recompile.
     */
    private void createSourceFileConst() {
        String[] doc = {
            "Field containing CLASSPATH relative name of the source file",
            "that this class can be regenerated from."
        };
        JavaField field = 
            new JavaField(XMLObject.XMLC_SOURCE_FILE_FIELD_NAME,
                          "String",
                          JavaModifiers.PUBLIC_CONST | JavaModifiers.OMIT_INTERFACE,
                          doc,
                          JavaLang.createStringConst(fMetaData.getInputDocument().getRecompileSource()));
        fDocClass.addField(field);
    }

    /**
     * Create the static field containing the XMLCDomFactory for the
     * class and the instance method to access it.
     */
    private void createDomFactoryField() {
        String[] doc = {
            "XMLC DOM factory associated with this class.",
        };
        String init
            = XMLCDomFactoryCache.class.getName()
            + ".getFactory(" + fXmlcDoc.getDomFactory().getClass().getName()
            + ".class)";

        JavaField field = 
            new JavaField(DOM_FACTORY_FIELD_NAME,
                          XMLCDomFactory.class.getName(),
                          JavaModifiers.PRIVATE | JavaModifiers.STATIC | JavaModifiers.FINAL,
                          doc, init);
        fDocClass.addField(field);

        JavaMethod method
            = new JavaMethod("getDomFactory",
                             XMLCDomFactory.class.getName(),
                             JavaModifiers.PROTECTED | JavaModifiers.FINAL,
                             null,
                             new String[] {"Get the XMLC DOM factory associated with the class."});
        fDocClass.addMethod(method);
        JavaCode body = method.getCode();
        body.addln("return " + DOM_FACTORY_FIELD_NAME + ";");
    }

    /**
     * Create the DOM building function.  This creates the base
     * method and delegate check, then class the DOM-specific
     * builder generator.
     */
    private void createBuildDocument() throws XMLCException {
        JavaMethod method
            = new JavaMethod("buildDocument",
                             "void",
                             JavaModifiers.PUBLIC | JavaModifiers.OMIT_INTERFACE,
                             null,
                             new String[] {"Create document as a DOM and initialize accessor method fields."});
        fDocClass.addMethod(method);
        JavaCode body = method.getCode();

        if (fDocumentClass.getDelegateSupport()) {
            body.addln(new String[] {
                "if (" + DELEGATE_FIELD_NAME + " != null) {",
                    "    " + DELEGATE_FIELD_NAME + ".buildDocument();",
                    "    return;",
                "}"});
        }
        fDocBuilderGenerator.createBuildDocumentMethod(fXmlcDoc,
                                                       fAccessorGenerator,
                                                       fElementTable,
                                                       fDocClass,
                                                       method);
    }

    /**
     * Create the class object.
     */
    private void createClass() throws XMLCException {
        XMLCDomFactory domFactory = fXmlcDoc.getDomFactory();

        // NB: Must replace the backslash in window file name in comment or
        // the JBuild compiler tries to treat it as an escape character.
        String[] doc = {
            "XMLC Document class, generated from",
            fMetaData.getInputDocument().getUrl().replace('\\','/')
        };

        fDocClass= new JavaClass(fDocumentClass.getPackageName(),
                                 fDocumentClass.getUnqualClassName(),
                                 JavaModifiers.PUBLIC,
                                 doc);
        // Interface name for generated interface.
        if (fDocumentClass.getUnqualInterfaceName() != null) {
            fDocClass.setInterface(fDocumentClass.getUnqualInterfaceName());
        }

        // Imports
        fDocClass.addImports(IMPORT_LIST);
        fDocClass.addClassImports(CLASS_IMPORT_LIST);
	if (fDocumentClass.getDeferredParsing()) {
	    fDocClass.addClassImports(DEFERRED_PARSING_CLASS_IMPORT_LIST);
	}
	
        // Determine class that will be extended
        String baseClassName = fDocumentClass.getExtends();
        if (baseClassName == null) {
            baseClassName = domFactory.getBaseClassName();
        }
        fDocClass.setExtends(baseClassName);

        // Determine interfaces to be implemented.
        fDocClass.addImplements(XMLObject.class.getName());
        String[] faces = domFactory.getInterfaceNames();
        if (faces != null) {
            fDocClass.addImplements(faces);
        }
        faces = fDocumentClass.getImplements();
        if (faces != null) {
            fDocClass.addImplements(faces);
        }
    }

    /**
     * Generate the class source.
     */
    private void generateClassSource(PrintWriter verboseOut)
        throws XMLCException, IOException {

        File src = fDocumentClass.getJavaClassSource();
        if (verboseOut != null) {
            // verboseOut.println("    creating class " +  fDocumentClass.getUnqualClassName()  + " at " + src.getAbsolutePath());
	    verboseOut.println("    creating class: " + src.getPath());
        }
        IndentWriter out = new IndentWriter(src, "ISO-8859-1");
        boolean finishedOk = false; // determine if all is ok
        try {
            out.println(WARNING_HEADER);
            if (fGenerate == GenerateType.IMPLEMENTATION) {
                fDocClass.printImplementation(out);
            } else {
                fDocClass.printClass(out);
            }
            finishedOk = true; // must be last in this block
        } finally {
            out.close(!finishedOk);
        }
    }

    /**
     * Generate an interface source and write to a file.
     */
    private void generateInterfaceSource(PrintWriter verboseOut)
        throws XMLCException, IOException {

        File src = fDocumentClass.getJavaInterfaceSource();
        if (verboseOut != null) {
            verboseOut.println("    creating interface: " + src.getAbsolutePath());
        }
        IndentWriter out = new IndentWriter(src, "ISO-8859-1");
        boolean finishedOk = false; // determine if all is ok
        try {
            out.println(WARNING_HEADER);
            fDocClass.printInterface(out);
            finishedOk = true; // must be last in this block
        } finally {
            out.close(!finishedOk);
        }
    }

    /*
     * Generate the Java source files.
     */
    public void generateJavaSource(PrintWriter verboseOut)
        throws XMLCException, IOException {

        if (fGenerate.generateClass()) {
            generateClassSource(verboseOut);
        }
        if (fGenerate.generateInterface()) {
            generateInterfaceSource(verboseOut);
        }
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy