org.tonguetied.datatransfer.DataServiceImpl.java Source code

Java tutorial

Introduction

Here is the source code for org.tonguetied.datatransfer.DataServiceImpl.java

Source

/*
 * Copyright 2008 The Tongue-Tied Authors
 * 
 * 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.tonguetied.datatransfer;

import static fmpp.setting.Settings.NAME_DATA;
import static fmpp.setting.Settings.NAME_OUTPUT_ENCODING;
import static fmpp.setting.Settings.NAME_OUTPUT_ROOT;
import static fmpp.setting.Settings.NAME_REPLACE_EXTENSIONS;
import static fmpp.setting.Settings.NAME_SOURCES;
import static fmpp.setting.Settings.NAME_SOURCE_ROOT;
import static freemarker.log.Logger.LIBRARY_LOG4J;

import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.zip.ZipEntry;
import java.util.zip.ZipOutputStream;

import org.apache.commons.io.FileUtils;
import org.apache.commons.io.IOUtils;
import org.apache.commons.lang.SystemUtils;
import org.apache.log4j.Logger;
import org.springframework.core.io.Resource;
import org.tonguetied.datatransfer.common.ExportParameters;
import org.tonguetied.datatransfer.common.FormatType;
import org.tonguetied.datatransfer.common.ImportParameters;
import org.tonguetied.datatransfer.dao.TransferRepository;
import org.tonguetied.datatransfer.exporting.ExportDataPostProcessor;
import org.tonguetied.datatransfer.exporting.ExportDataPostProcessorFactory;
import org.tonguetied.datatransfer.exporting.ExportException;
import org.tonguetied.datatransfer.exporting.Native2AsciiDirective;
import org.tonguetied.datatransfer.exporting.NoExportDataException;
import org.tonguetied.datatransfer.importing.Importer;
import org.tonguetied.datatransfer.importing.ImporterFactory;
import org.tonguetied.keywordmanagement.KeywordService;
import org.tonguetied.keywordmanagement.Translation;

import fmpp.ProcessingException;
import fmpp.progresslisteners.LoggerProgressListener;
import fmpp.setting.SettingException;
import fmpp.setting.Settings;

/**
 * Concrete implementation of the {@link DataService} interface.
 * 
 * @author bsion
 *
 */
public class DataServiceImpl implements DataService {
    private Settings settings;
    private TransferRepository transferRepository;
    private KeywordService keywordService;
    private File sourceRoot;
    private File outputRoot;
    private File outputDir;

    private static final File BASE_DIR = SystemUtils.getUserDir();
    private static final Logger logger = Logger.getLogger(DataServiceImpl.class);
    private static final String DATE_FORMAT = "yyyy-MM-dd_HH_mm_ss";

    /**
     * Create a new instance of the DataServiceImpl. After this constructor
     * has been called the {@link #init()} method should be called.
     */
    public DataServiceImpl() {
    }

    /**
     * Initialize an instance of the DataServiceImpl. This method configures
     * the exporter for use.
     *  
     * @throws ExportException if the exporter is fails to configure
     */
    public void init() throws ExportException {
        if (logger.isDebugEnabled())
            logger.debug("loading freemarker settings");
        try {
            settings = new Settings(BASE_DIR);
            settings.set(NAME_SOURCE_ROOT, sourceRoot.getPath());
            settings.set(NAME_OUTPUT_ENCODING, "UTF-8");
            freemarker.log.Logger.selectLoggerLibrary(LIBRARY_LOG4J);
            createOutputDirectory();
        } catch (SettingException se) {
            throw new ExportException(se);
        } catch (ClassNotFoundException cnfe) {
            throw new ExportException(cnfe);
        }
    }

    /**
     * Create the output root directory if doesn't already exist.
     */
    private void createOutputDirectory() {
        if (!outputRoot.exists()) {
            if (outputRoot.mkdirs())
                if (logger.isInfoEnabled())
                    logger.info("created directory " + outputRoot.getPath());
        }
    }

    public void exportData(final ExportParameters parameters) throws ExportException {
        if (parameters == null) {
            throw new IllegalArgumentException("cannot perform export with " + "null parameters");
        }
        if (parameters.getFormatType() == null) {
            throw new IllegalArgumentException("cannot perform export without" + " an export type set");
        }
        final long start = System.currentTimeMillis();

        if (logger.isDebugEnabled())
            logger.debug("exporting based on filter " + parameters);

        try {
            List<Translation> translations = transferRepository.findTranslations(parameters);
            if (translations.isEmpty()) {
                throw new NoExportDataException(parameters);
            }

            File exportPath = getExportPath(true);
            final boolean isDirCreated = exportPath.mkdir();
            if (!isDirCreated)
                logger.warn("failed to create directory: " + exportPath);
            settings.set(NAME_OUTPUT_ROOT, exportPath.getAbsolutePath());
            settings.set(NAME_SOURCES, getTemplateName(parameters.getFormatType()));
            String[] replaceExtensions = new String[] { "ftl",
                    parameters.getFormatType().getDefaultFileExtension() };
            settings.set(NAME_REPLACE_EXTENSIONS, replaceExtensions);

            Map<String, Object> root = postProcess(parameters, translations);
            // TODO: follow best practice and put in the configuration as a 
            // shared variable, see: http://freemarker.sourceforge.net/docs/pgui_datamodel_directive.html
            root.put("native2ascii", new Native2AsciiDirective());
            settings.set(NAME_DATA, root);
            settings.addProgressListener(new LoggerProgressListener());
            settings.execute();

            if (parameters.isResultPackaged()) {
                createArchive(exportPath);
            }
        } catch (SettingException se) {
            throw new ExportException(se);
        } catch (ProcessingException pe) {
            throw new ExportException(pe);
        }

        if (logger.isInfoEnabled()) {
            final float totalMillis = System.currentTimeMillis() - start;
            logger.info("export complete in " + (totalMillis / 1000) + " seconds");
        }
    }

    /**
     * Post process the result translations to put them into a desired format 
     * if needed.
     * 
     * @param parameters the parameters used to filter and format the data
     * @param translations the {@link Translation}s to process
     * @return a map of parameters used by the templating mechanism
     */
    private Map<String, Object> postProcess(final ExportParameters parameters, List<Translation> translations) {
        Map<String, Object> root = new HashMap<String, Object>();
        final ExportDataPostProcessor postProcessor = ExportDataPostProcessorFactory
                .getPostProcessor(parameters.getFormatType(), parameters, keywordService);
        if (postProcessor != null) {
            if (logger.isDebugEnabled())
                logger.debug("post processing results using: " + postProcessor.getClass());

            final List<?> results = postProcessor.transformData(translations);

            root.put("translations", results);
            postProcessor.addItems(root);
        } else {
            root.put("translations", translations);
        }

        return root;
    }

    public void createArchive(File directory) throws ExportException, IllegalArgumentException {
        if (!directory.isDirectory())
            throw new IllegalArgumentException("expecting a directory");

        ZipOutputStream zos = null;
        try {
            File[] files = directory.listFiles();
            if (files.length > 0) {
                final File archive = new File(directory, directory.getName() + ".zip");
                zos = new ZipOutputStream(new FileOutputStream(archive));
                for (File file : files) {
                    zos.putNextEntry(new ZipEntry(file.getName()));
                    IOUtils.write(FileUtils.readFileToByteArray(file), zos);
                    zos.closeEntry();
                }

                if (logger.isDebugEnabled())
                    logger.debug("archived " + files.length + " files to " + archive.getPath());
            }
        } catch (IOException ioe) {
            throw new ExportException(ioe);
        } finally {
            IOUtils.closeQuietly(zos);
        }
    }

    /**
     * Returns the the directory where exported files from the most recently 
     * executed export are saved. This method passes a value of false to 
     * {@link #getExportPath(boolean)} so as not to reset the output path.
     * 
     * @return the output directory
     * @see #getExportPath(boolean) 
     */
    public File getExportPath() {
        return getExportPath(false);
    }

    /**
     * Returns the the directory where exported files from the most recently 
     * executed export are saved.
     * 
     * @param reset flag indicating that the output directory should be 
     * re-initialised.
     * @return the output directory 
     */
    private File getExportPath(final boolean reset) {
        if (reset) {
            final DateFormat formatter = new SimpleDateFormat(DATE_FORMAT);
            outputDir = new File(outputRoot, formatter.format(new Date()));
        }

        return outputDir;
    }

    public void importData(final ImportParameters parameters) {
        final long start = System.currentTimeMillis();
        if (logger.isDebugEnabled())
            logger.debug("importing based on filter " + parameters);

        Importer importer = ImporterFactory.getImporter(parameters.getFormatType(), keywordService,
                transferRepository);
        importer.importData(parameters);

        if (logger.isInfoEnabled()) {
            final long totalMillis = System.currentTimeMillis() - start;
            logger.info("import complete in " + (totalMillis / 1000) + " seconds");
        }
    }

    /**
     * Determine the name of the export template to use based off the type of
     * export being performed.
     * 
     * @param formatType the type of export being performed
     * @return the name of the export template to use
     */
    private String getTemplateName(final FormatType formatType) {
        return formatType.name() + ".ftl";
    }

    public void setTransferRepository(final TransferRepository transferRepository) {
        this.transferRepository = transferRepository;
    }

    /**
     * Assign the directory containing the templates.
     * 
     * @param sourceRoot the directory on the file system where template files
     * are stored 
     */
    public void setSourceRoot(final Resource sourceRoot) {
        this.sourceRoot = getFile(sourceRoot);
    }

    /**
     * @param outputRoot the base directory on the file system where all 
     * generated export files should be saved.
     */
    public void setOutputRoot(final Resource outputRoot) {
        this.outputRoot = getFile(outputRoot);
    }

    /**
     * Get the file object from the resource
     * 
     * @param resource the resource object from which to get the file
     * @throws ExportException thrown if an error occurs trying to get the file
     * from the resource
     */
    private File getFile(final Resource resource) throws ExportException {
        try {
            return resource.getFile();
        } catch (IOException ioe) {
            throw new ExportException(ioe);
        }
    }

    /**
     * @param keywordService the keywordService to set
     */
    public void setKeywordService(final KeywordService keywordService) {
        this.keywordService = keywordService;
    }
}