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

com.jaxio.celerio.output.FolderOutputResult Maven / Gradle / Ivy

There is a newer version: 4.0.23
Show newest version
/*
 * Copyright 2015 JAXIO http://www.jaxio.com
 *
 * 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 com.jaxio.celerio.output;

import com.jaxio.celerio.template.pack.Template;
import com.jaxio.celerio.template.pack.TemplatePack;
import lombok.extern.slf4j.Slf4j;

import java.io.*;
import java.util.HashMap;

import static com.google.common.collect.Maps.newHashMap;
import static com.jaxio.celerio.convention.WellKnownFolder.JAVA;
import static com.jaxio.celerio.convention.WellKnownFolder.JAVA_TEST;
import static org.apache.commons.io.FilenameUtils.normalize;
import static org.apache.commons.io.IOUtils.*;

@Slf4j
public class FolderOutputResult implements OutputResult {
    private FileTracker fileTracker;
    private SourceFile userSource;
    private SourceFile generatedSource;
    private boolean sameDirectory;

    private boolean isOpen = false;
    private boolean fileMetaDataSavedOk;

    private HashMap generatedFiles = newHashMap();

    public FolderOutputResult(SourceFile userSource, SourceFile generatedSource) {
        this(userSource, generatedSource, null);
    }

    public FolderOutputResult(SourceFile userSource, SourceFile generatedSource, FileTracker fileTracker) {
        this.userSource = userSource;
        this.generatedSource = generatedSource;
        this.fileTracker = fileTracker;
        this.sameDirectory = userSource.getDirectory().equals(generatedSource.getDirectory());
    }

    @Override
    public void open() throws IOException {
        if (isOpen) {
            return;
        }

        fileMetaDataSavedOk = false;

        // NOTE: if we don't do this, we end up with inconsistent state
        // For example if the user Ctrl-C while Celerio generates the files,
        // we loose the generation information if we do not catch the shutdown.
        // As a result, during the next generation, Celerio thinks the files have
        // been manually modified... So with this hook, hopefully we record
        // on disk whatever was generated before the interruption.
        Runtime.getRuntime().addShutdownHook(new Thread() {
            public void run() {
                if (isFileTrackingEnabled() && !fileMetaDataSavedOk) {
                    try {
                        close();
                    } catch (IOException ioe) {
                        // well, too bad...
                        log.error("Could not write to disk last generation trace: " + ioe.getMessage());
                    }
                }
            }
        });

        if (isFileTrackingEnabled()) {
            // it is important to keep existing data to handle cases where user generates
            // several times in a raw before cleaning... indeed, the second time he generates
            // the files is not written to disk, as it is identical...
            generatedFiles = fileTracker.loadFromProjectDir(new File(userSource.getDirectory()));
        }
        isOpen = true;
    }

    synchronized public void close() throws IOException {
        if (isOpen) {
            isOpen = false;
            saveFileMetaData();
        }
    }

    private void saveFileMetaData() throws IOException {
        if (isFileTrackingEnabled() && !fileMetaDataSavedOk) {
            // remember what was generated by dumping it in a special file.
            fileTracker.saveToProjectDir(generatedFiles, new File(userSource.getDirectory()));
            fileMetaDataSavedOk = true;
        }
    }

    @Override
    public void addContent(byte[] contentBytes, String targetFilename, TemplatePack pack, Template template) throws IOException {
        String fullPath = generatedSource.getFullPath(targetFilename);
        addContentWithFullPath(contentBytes, fullPath, pack, template);
    }

    @Override
    public void addCollisionContent(byte[] contentBytes, String targetFilename, TemplatePack pack, Template template) throws IOException {
        String fullPath = userSource.getFullPath(targetFilename);
        addContentWithFullPath(contentBytes, fullPath, pack, template);
    }

    private void addContentWithFullPath(byte[] contentBytes, String fullPath, TemplatePack pack, Template template) throws IOException {
        open();

        File targetFile = new File(fullPath);

        // create folder if needed
        File parent = targetFile.getParentFile();
        if (parent != null) {
            parent.mkdirs();
        } // else child was for example "."

        FileOutputStream targetFileOutput = new FileOutputStream(targetFile);

        write(contentBytes, targetFileOutput);
        closeQuietly(targetFileOutput);

        // keep track of generated file so we can easily delete it
        if (isFileTrackingEnabled()) {
            String filePathRelativeToUserSrc = getPathRelativeToBase(targetFile, userSource.getDirectory());
            generatedFiles.put(filePathRelativeToUserSrc, new FileMetaData(pack, template, filePathRelativeToUserSrc, targetFile));
        }
    }

    private String getPathRelativeToBase(File targetFile, String basePath) {
        String absoluteBasePath = new File(basePath).getAbsolutePath();
        String absolutePath = targetFile.getAbsolutePath();
        if (absolutePath.startsWith(absoluteBasePath)) {
            String result = absolutePath.substring(absoluteBasePath.length());
            if (result.charAt(0) == File.separatorChar) {
                result = result.substring(1);
            }
            return result;
        }
        throw new IllegalStateException("File is not under absoluteBasePath path!: " + absolutePath + " absoluteBasePath: " + absoluteBasePath);
    }

    @Override
    public void addContent(InputStream contentStream, String targetFilename, TemplatePack pack, Template template) throws IOException {
        addContent(toByteArray(new InputStreamReader(contentStream), "UTF-8"), targetFilename, pack, template);
    }

    @Override
    public SourceFile getUserSource() {
        return userSource;
    }

    @Override
    public SourceFile getGeneratedSource() {
        return generatedSource;
    }

    @Override
    public boolean sameDirectory() {
        return sameDirectory;
    }

    /**
     * @param pathToFile relative path to the file to check. Relative to the folder containing the ".celerio/generated.xml"
     */
    private boolean manualModificationSinceLastGeneration(String pathToFile) {
        if (!isFileTrackingEnabled()) {
            return true; // no risk
        }

        FileMetaData oldFmd = generatedFiles.get(pathToFile);
        if (oldFmd == null) {
            return false;
        }

        if (!generatedSource.fileExists(pathToFile)) {
            return false;
        }

        FileMetaData recentFmd = new FileMetaData(null, null, pathToFile, new File(generatedSource.getFullPath(pathToFile)));

        // not equals means there is a chance that the user manually edited the file between 2 generations.
        boolean areNotEqual = !oldFmd.equals(recentFmd);

        if (areNotEqual && log.isInfoEnabled()) {
            log.info("MANUAL MODIFICATION detected for: " + pathToFile);
        }

        return areNotEqual;
    }

    /**
     * @return true if the file is located under one of these directories: src/main/java or src/test/java
     */
    @Override
    public boolean hasCollision(String pathToFile) {
        if (sameDirectory()) {
            return isImportantUserFile(pathToFile) || manualModificationSinceLastGeneration(pathToFile);
        } else {
            return manualModificationSinceLastGeneration(pathToFile);
        }
    }

    @Override
    public String getCollisionName(String targetFilename) {
        return normalize("target" + File.separatorChar + "celerio-maven-plugin" + File.separatorChar + "collisions" + File.separatorChar + targetFilename);
    }

    /**
     * @return true if the file is located under one of these directories: src/main/java or src/test/java
     */
    private boolean isImportantUserFile(String outputFile) {
        return userSource.fileExists(outputFile) && (isInJavaUserOnlyFolder(outputFile) || isImportantFile(outputFile));
    }

    boolean isInJavaUserOnlyFolder(String outputFile) {
        String normalizedOutputFile = normalize(outputFile);
        boolean isInJava = normalizedOutputFile.contains(JAVA.getFolder());
        boolean isInJavaTest = normalizedOutputFile.contains(JAVA_TEST.getFolder());
        return isInJava || isInJavaTest;
    }

    boolean isImportantFile(String outputFile) {
        return outputFile.contains("pom.xml") || outputFile.contains("celerio-maven-plugin.xml") || outputFile.contains("celerio-template-packs.xml")
                || outputFile.contains("01-create.sql");
    }

    private boolean isFileTrackingEnabled() {
        return fileTracker != null;
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy