Package org.mybatis.generator.eclipse.core.merge

Source Code of org.mybatis.generator.eclipse.core.merge.JavaFileMerger

/*
*  Copyright 2005, 2006, 2008 The Apache Software Foundation
*
*  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.
*/
package org.mybatis.generator.eclipse.core.merge;

import java.io.BufferedReader;
import java.io.File;
import java.io.FileReader;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;

import org.eclipse.jdt.core.JavaCore;
import org.eclipse.jdt.core.dom.AST;
import org.eclipse.jdt.core.dom.ASTNode;
import org.eclipse.jdt.core.dom.ASTParser;
import org.eclipse.jdt.core.dom.CompilationUnit;
import org.eclipse.jdt.core.dom.ImportDeclaration;
import org.eclipse.jdt.core.dom.Modifier;
import org.eclipse.jdt.core.dom.Name;
import org.eclipse.jdt.core.dom.SimpleType;
import org.eclipse.jdt.core.dom.Type;
import org.eclipse.jdt.core.dom.TypeDeclaration;
import org.eclipse.jdt.core.dom.rewrite.ASTRewrite;
import org.eclipse.jdt.core.dom.rewrite.ListRewrite;
import org.eclipse.jface.text.BadLocationException;
import org.eclipse.jface.text.Document;
import org.eclipse.jface.text.IDocument;
import org.eclipse.text.edits.TextEdit;
import org.mybatis.generator.exception.ShellException;

/**
* This class handles the task of merging changes into an existing Java file.
*
* This class makes several assumptions about the structure of the new and
* existing files, including:
*
* <ul>
*   <li>The imports of both files are fully qualified (no wildcard imports)</li>
*   <li>The super interfaces of both files are NOT fully qualified</li>
*   <li>The super classes of both files are NOT fully qualified</li>
* </ul>
*
* @author Jeff Butler
*/
public class JavaFileMerger {
   
    private String newJavaSource;
    private String existingFilePath;
    private String[] javaDocTags;

    public JavaFileMerger(String newJavaSource, String existingFilePath,
            String[] javaDocTags) {
        super();
        this.newJavaSource = newJavaSource;
        this.existingFilePath = existingFilePath;
        this.javaDocTags = javaDocTags;
    }

    @SuppressWarnings({ "unchecked", "rawtypes" })
    public String getMergedSource() throws ShellException {
        ASTParser astParser = ASTParser.newParser(AST.JLS3);
        NewJavaFileVisitor newJavaFileVisitor = visitNewJavaFile(astParser);

        String existingFile = getExistingFileContents();
        IDocument document = new Document(existingFile);

        // delete generated stuff, and collect imports
        ExistingJavaFileVisitor visitor = new ExistingJavaFileVisitor(javaDocTags);

        astParser.setSource(existingFile.toCharArray());
        CompilationUnit cu = (CompilationUnit) astParser.createAST(null);
        AST ast = cu.getAST();
        cu.recordModifications();
        cu.accept(visitor);

        TypeDeclaration typeDeclaration = visitor.getTypeDeclaration();
        if (typeDeclaration == null) {
            StringBuffer sb = new StringBuffer();
            sb.append("No types defined in the file ");
            sb.append(existingFilePath);

            throw new ShellException(sb.toString());
        }

        // reconcile the superinterfaces
        List<Type> newSuperInterfaces = getNewSuperInterfaces(typeDeclaration
                .superInterfaceTypes(), newJavaFileVisitor);
        for (Type newSuperInterface : newSuperInterfaces) {
            if (newSuperInterface.isSimpleType()) {
                SimpleType st = (SimpleType) newSuperInterface;
                Name name = ast.newName(st.getName().getFullyQualifiedName());
                SimpleType newSt = ast.newSimpleType(name);
                typeDeclaration.superInterfaceTypes().add(newSt);
            } else {
                // this shouldn't happen - MyBatis Generator only generates simple names
                throw new ShellException("The Java file merger only supports simple types as super interfaces");
            }
        }

        // set the superclass
        if (newJavaFileVisitor.getSuperclass() != null) {
            if (newJavaFileVisitor.getSuperclass().isSimpleType()) {
                SimpleType st = (SimpleType) newJavaFileVisitor.getSuperclass();
                Name name = ast.newName(st.getName().getFullyQualifiedName());
                SimpleType newSt = ast.newSimpleType(name);
                typeDeclaration.setSuperclassType(newSt);
            } else {
                // this shouldn't happen - MyBatis Generator only generates simple names
                throw new ShellException("The Java file merger only supports simple types as super classes");
            }
        } else {
            typeDeclaration.setSuperclassType(null);
        }

        // interface or class?
        if (newJavaFileVisitor.isInterface()) {
            typeDeclaration.setInterface(true);
        } else {
            typeDeclaration.setInterface(false);
        }

        // reconcile the imports
        List<ImportDeclaration> newImports = getNewImports(cu.imports(), newJavaFileVisitor);
        for (ImportDeclaration newImport : newImports) {
            Name name = ast.newName(newImport.getName().getFullyQualifiedName());
            ImportDeclaration newId = ast.newImportDeclaration();
            newId.setName(name);
            cu.imports().add(newId);
        }

        TextEdit textEdit = cu.rewrite(document, null);
        try {
            textEdit.apply(document);
        } catch (BadLocationException e) {
            throw new ShellException(
                    "BadLocationException removing prior fields and methods");
        }

        // regenerate the CompilationUnit to reflect all the deletes and changes
        astParser.setSource(document.get().toCharArray());
        CompilationUnit strippedCu = (CompilationUnit) astParser.createAST(null);

        // find the top level public type declaration
        TypeDeclaration topLevelType = null;
        Iterator iter = strippedCu.types().iterator();
        while (iter.hasNext()) {
            TypeDeclaration td = (TypeDeclaration) iter.next();
            if (td.getParent().equals(strippedCu)
                    && (td.getModifiers() & Modifier.PUBLIC) > 0) {
                topLevelType = td;
                break;
            }
        }

        // now add all the new methods and fields to the existing
        // CompilationUnit with a ListRewrite
        ASTRewrite rewrite = ASTRewrite.create(topLevelType.getRoot().getAST());
        ListRewrite listRewrite = rewrite.getListRewrite(topLevelType,
                TypeDeclaration.BODY_DECLARATIONS_PROPERTY);

        Iterator<ASTNode> astIter = newJavaFileVisitor.getNewNodes().iterator();
        int i = 0;
        while (astIter.hasNext()) {
            ASTNode node = astIter.next();
           
            if (node.getNodeType() == ASTNode.TYPE_DECLARATION) {
                String name = ((TypeDeclaration) node).getName().getFullyQualifiedName();
                if (visitor.containsInnerClass(name)) {
                    continue;
                }
            }
           
            listRewrite.insertAt(node, i++, null);
        }

        textEdit = rewrite.rewriteAST(document, JavaCore.getOptions());
        try {
            textEdit.apply(document);
        } catch (BadLocationException e) {
            throw new ShellException(
                    "BadLocationException adding new fields and methods");
        }

        String newSource = document.get();
        return newSource;
    }

    private List<Type> getNewSuperInterfaces(List<Type> existingSuperInterfaces,
            NewJavaFileVisitor newJavaFileVisitor) {

        List<Type> answer = new ArrayList<Type>();

        for (Type newSuperInterface : newJavaFileVisitor.getSuperInterfaceTypes()) {
            if (newSuperInterface.isSimpleType()) {
                SimpleType newSimpleType = (SimpleType) newSuperInterface;
                String newName = newSimpleType.getName().getFullyQualifiedName();
               
                boolean found = false;
                for (Type existingSuperInterface : existingSuperInterfaces) {
                    if (existingSuperInterface.isSimpleType()) {
                        SimpleType existingSimpleType = (SimpleType) existingSuperInterface;

                        String existingName = existingSimpleType.getName().getFullyQualifiedName();

                        if (newName.equals(existingName)) {
                            found = true;
                            break;
                        }
                    }
                }
               
                if (!found) {
                    answer.add(newSuperInterface);
                }
            }
        }

        return answer;
    }

    private List<ImportDeclaration> getNewImports(List<ImportDeclaration> existingImports,
            NewJavaFileVisitor newJavaFileVisitor) {
        List<ImportDeclaration> answer = new ArrayList<ImportDeclaration>();

        for (ImportDeclaration newImport : newJavaFileVisitor.getImports()) {
            String newName = newImport.getName().getFullyQualifiedName();
            boolean found = false;
            for (ImportDeclaration existingImport : existingImports) {
                String existingName = existingImport.getName().getFullyQualifiedName();
               
                if (newName.equals(existingName)) {
                    found = true;
                    break;
                }
            }
           
            if (!found) {
                answer.add(newImport);
            }
        }
       
        return answer;
    }

    /**
     * This method parses the new Java file and returns a
     * filled out NewJavaFileVisitor.  The returned visitor can
     * be used to determine characteristics of the new file, and
     * a lost of new nodes that need to be incorporated into the
     * existing file.
     *
     * @param astParser
     * @return
     */
    private NewJavaFileVisitor visitNewJavaFile(ASTParser astParser) {
        astParser.setSource(newJavaSource.toCharArray());
        CompilationUnit cu = (CompilationUnit) astParser.createAST(null);
        NewJavaFileVisitor newVisitor = new NewJavaFileVisitor();
        cu.accept(newVisitor);
       
        return newVisitor;
    }
   
    private String getExistingFileContents() throws ShellException {
        File file = new File(existingFilePath);
       
        if (!file.exists()) {
            // this should not happen because MyBatis Generator only returns the path
            // calculated by the eclipse callback
            StringBuilder sb = new StringBuilder();
            sb.append("The file ");
            sb.append(existingFilePath);
            sb.append(" does not exist");
            throw new ShellException(sb.toString());
        }

        try {
            StringBuilder sb = new StringBuilder();
            BufferedReader br = new BufferedReader(new FileReader(file));
            char[] buffer = new char[1024];
            int returnedBytes = br.read(buffer);
            while (returnedBytes != -1) {
                sb.append(buffer, 0, returnedBytes);
                returnedBytes = br.read(buffer);
            }
       
            br.close();
            return sb.toString();
        } catch (IOException e) {
            StringBuilder sb = new StringBuilder();
            sb.append("IOException reading the file ");
            sb.append(existingFilePath);
            throw new ShellException(sb.toString(), e);
        }
    }
}
TOP

Related Classes of org.mybatis.generator.eclipse.core.merge.JavaFileMerger

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.