net.sf.eclipsecs.core.projectconfig.ProjectConfigurationWorkingCopy.java Source code

Java tutorial

Introduction

Here is the source code for net.sf.eclipsecs.core.projectconfig.ProjectConfigurationWorkingCopy.java

Source

//============================================================================
//
// Copyright (C) 2002-2014  David Schneider, Lars Kdderitzsch
//
// This library is free software; you can redistribute it and/or
// modify it under the terms of the GNU Lesser General Public
// License as published by the Free Software Foundation; either
// version 2.1 of the License, or (at your option) any later version.
//
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
// Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
//
//============================================================================

package net.sf.eclipsecs.core.projectconfig;

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;

import net.sf.eclipsecs.core.Messages;
import net.sf.eclipsecs.core.config.CheckConfigurationFactory;
import net.sf.eclipsecs.core.config.CheckConfigurationWorkingCopy;
import net.sf.eclipsecs.core.config.ICheckConfiguration;
import net.sf.eclipsecs.core.config.ICheckConfigurationWorkingSet;
import net.sf.eclipsecs.core.config.ResolvableProperty;
import net.sf.eclipsecs.core.config.configtypes.BuiltInConfigurationType;
import net.sf.eclipsecs.core.config.configtypes.ProjectConfigurationType;
import net.sf.eclipsecs.core.projectconfig.filters.IFilter;
import net.sf.eclipsecs.core.util.CheckstylePluginException;
import net.sf.eclipsecs.core.util.XMLUtil;

import org.apache.commons.io.IOUtils;
import org.apache.commons.lang.builder.EqualsBuilder;
import org.apache.commons.lang.builder.HashCodeBuilder;
import org.dom4j.Document;
import org.dom4j.DocumentHelper;
import org.dom4j.Element;
import org.eclipse.core.resources.IFile;
import org.eclipse.core.resources.IProject;
import org.eclipse.core.resources.IResource;
import org.eclipse.core.resources.IWorkspaceRoot;
import org.eclipse.core.runtime.Path;
import org.eclipse.osgi.util.NLS;

/**
 * A modifiable project configuration implementation.
 * 
 * @author Lars Kdderitzsch
 */
public class ProjectConfigurationWorkingCopy implements Cloneable, IProjectConfiguration {

    //
    // attributes
    //

    /** The original, unmodified project configuration. */
    private final IProjectConfiguration mProjectConfig;

    /** The local check configurations. */
    private final ICheckConfigurationWorkingSet mLocalConfigWorkingSet;

    /** The global check configurations. */
    private final ICheckConfigurationWorkingSet mGlobalConfigWorkingSet;

    /** the file sets. */
    private List<FileSet> mFileSets = new LinkedList<FileSet>();

    /** the filters. */
    private List<IFilter> mFilters = new LinkedList<IFilter>();

    /** Flags if the simple file set editor should be used. */
    private boolean mUseSimpleConfig;

    /** if the formatter synching feature is enabled. */
    private boolean mSyncFormatter;

    //
    // constructors
    //

    /**
     * Creates a working copy of a given project configuration.
     * 
     * @param projectConfig
     *            the project configuration
     */
    public ProjectConfigurationWorkingCopy(IProjectConfiguration projectConfig) {

        mProjectConfig = projectConfig;

        mLocalConfigWorkingSet = new LocalCheckConfigurationWorkingSet(this,
                projectConfig.getLocalCheckConfigurations());
        mGlobalConfigWorkingSet = CheckConfigurationFactory.newWorkingSet();

        // clone file sets of the original config

        for (FileSet fileSet : projectConfig.getFileSets()) {
            mFileSets.add(fileSet.clone());
        }

        // build list of filters
        List<IFilter> standardFilters = Arrays.asList(PluginFilters.getConfiguredFilters());
        mFilters = new ArrayList<IFilter>(standardFilters);

        // merge with filters configured for the project
        List<IFilter> configuredFilters = projectConfig.getFilters();
        for (int i = 0, size = mFilters.size(); i < size; i++) {

            IFilter standardFilter = mFilters.get(i);

            for (int j = 0, size2 = configuredFilters.size(); j < size2; j++) {
                IFilter configuredFilter = configuredFilters.get(j);

                if (standardFilter.getInternalName().equals(configuredFilter.getInternalName())) {
                    mFilters.set(i, configuredFilter.clone());
                }
            }
        }

        mUseSimpleConfig = projectConfig.isUseSimpleConfig();
        mSyncFormatter = projectConfig.isSyncFormatter();
    }

    //
    // methods
    //

    /**
     * Returns the check configuration working set for local configurations.
     * 
     * @return the local configurations working set
     */
    public ICheckConfigurationWorkingSet getLocalCheckConfigWorkingSet() {
        return mLocalConfigWorkingSet;
    }

    /**
     * Returns the check configuration working set for global configurations.
     * 
     * @return the local configurations working set
     */
    public ICheckConfigurationWorkingSet getGlobalCheckConfigWorkingSet() {
        return mGlobalConfigWorkingSet;
    }

    /**
     * Returns a project local check configuration by its name.
     * 
     * @param name
     *            the configurations name
     * @return the check configuration or <code>null</code>, if no local
     *         configuration with this name exists
     */
    public ICheckConfiguration getLocalCheckConfigByName(String name) {
        ICheckConfiguration config = null;
        ICheckConfiguration[] configs = mLocalConfigWorkingSet.getWorkingCopies();
        for (int i = 0; i < configs.length; i++) {
            if (configs[i].getName().equals(name)) {
                config = configs[i];
                break;
            }
        }

        return config;
    }

    /**
     * Returns a project local check configuration by its name.
     * 
     * @param name
     *            the configurations name
     * @return the check configuration or <code>null</code>, if no local
     *         configuration with this name exists
     */
    public ICheckConfiguration getGlobalCheckConfigByName(String name) {
        ICheckConfiguration config = null;
        ICheckConfiguration[] configs = mGlobalConfigWorkingSet.getWorkingCopies();
        for (int i = 0; i < configs.length; i++) {
            if (configs[i].getName().equals(name)) {
                config = configs[i];
                break;
            }
        }

        return config;
    }

    /**
     * Sets if the simple configuration should be used.
     * 
     * @param useSimpleConfig
     *            true if the project uses the simple fileset configuration
     */
    public void setUseSimpleConfig(boolean useSimpleConfig) {
        mUseSimpleConfig = useSimpleConfig;
    }

    /**
     * Sets if the formatter synching is enabled.
     * 
     * @param syncFormatter
     *            true if the projects formatter settings should be synced with
     *            the Checkstyle config
     */
    public void setSyncFormatter(boolean syncFormatter) {
        mSyncFormatter = syncFormatter;
    }

    /**
     * Determines if the project configuration changed.
     * 
     * @return <code>true</code> if changed
     */
    public boolean isDirty() {
        return !this.equals(mProjectConfig) || mLocalConfigWorkingSet.isDirty();
    }

    /**
     * Determines if a rebuild is needed for the project of this project
     * configuration. A rebuild is not needed when only some local config was
     * added which is not used by the project.
     * 
     * @return <code>true</code> if rebuild is needed.
     * @throws CheckstylePluginException
     *             an unexpected exception occurred
     */
    public boolean isRebuildNeeded() throws CheckstylePluginException {
        return !this.equals(mProjectConfig) || mLocalConfigWorkingSet.getAffectedProjects().contains(getProject())
                || mGlobalConfigWorkingSet.getAffectedProjects().contains(getProject());
    }

    /**
     * Stores the project configuration.
     * 
     * @throws CheckstylePluginException
     *             error while storing the project configuration
     */
    public void store() throws CheckstylePluginException {
        storeToPersistence(this);
    }

    //
    // implementation of IProjectConfiguration interface
    //

    /**
     * {@inheritDoc}
     */
    public IProject getProject() {
        return mProjectConfig.getProject();
    }

    /**
     * {@inheritDoc}
     */
    public List<ICheckConfiguration> getLocalCheckConfigurations() {

        List<ICheckConfiguration> l = new ArrayList<ICheckConfiguration>();
        for (ICheckConfiguration c : mLocalConfigWorkingSet.getWorkingCopies()) {
            l.add(c);
        }

        return l;
    }

    /**
     * {@inheritDoc}
     */
    public List<FileSet> getFileSets() {
        return mFileSets;
    }

    /**
     * {@inheritDoc}
     */
    public List<IFilter> getFilters() {
        return mFilters;
    }

    /**
     * {@inheritDoc}
     */
    public boolean isUseSimpleConfig() {
        return mUseSimpleConfig;
    }

    /**
     * {@inheritDoc}
     */
    public boolean isSyncFormatter() {
        return mSyncFormatter;
    }

    /**
     * {@inheritDoc}
     */
    public boolean isConfigInUse(ICheckConfiguration configuration) {

        boolean result = false;

        for (FileSet fileSet : getFileSets()) {
            ICheckConfiguration checkConfig = fileSet.getCheckConfig();
            if (configuration.equals(checkConfig)
                    || (checkConfig instanceof CheckConfigurationWorkingCopy && configuration
                            .equals(((CheckConfigurationWorkingCopy) checkConfig).getSourceCheckConfiguration()))) {
                result = true;
                break;
            }
        }
        return result;
    }

    /**
     * {@inheritDoc}
     */
    public Object clone() {
        ProjectConfigurationWorkingCopy clone = null;
        try {
            clone = (ProjectConfigurationWorkingCopy) super.clone();
            clone.mFileSets = new LinkedList<FileSet>();
            clone.setUseSimpleConfig(this.isUseSimpleConfig());
            clone.setSyncFormatter(this.isSyncFormatter());

            // clone file sets
            for (FileSet fileSet : getFileSets()) {
                clone.getFileSets().add(fileSet.clone());
            }

            // clone filters
            List<IFilter> clonedFilters = new ArrayList<IFilter>();
            for (IFilter filter : getFilters()) {
                clonedFilters.add(filter.clone());
            }
            clone.mFilters = clonedFilters;
        } catch (CloneNotSupportedException e) {
            throw new InternalError();
        }

        return clone;
    }

    /**
     * {@inheritDoc}
     */
    public boolean equals(Object obj) {

        if (obj == null || !(obj instanceof IProjectConfiguration)) {
            return false;
        }
        if (this == obj) {
            return true;
        }
        IProjectConfiguration rhs = (IProjectConfiguration) obj;
        return new EqualsBuilder().append(getProject(), rhs.getProject())
                .append(isUseSimpleConfig(), rhs.isUseSimpleConfig())
                .append(isSyncFormatter(), rhs.isSyncFormatter()).append(getFileSets(), rhs.getFileSets())
                .append(getFilters(), rhs.getFilters()).isEquals();
    }

    /**
     * {@inheritDoc}
     */
    public int hashCode() {
        return new HashCodeBuilder(984759323, 1000003).append(mProjectConfig).append(mUseSimpleConfig)
                .append(mFileSets).append(mFilters).toHashCode();
    }

    /**
     * Store the audit configurations to the persistent state storage.
     */
    private void storeToPersistence(ProjectConfigurationWorkingCopy config) throws CheckstylePluginException {

        ByteArrayOutputStream pipeOut = null;
        InputStream pipeIn = null;
        try {

            pipeOut = new ByteArrayOutputStream();

            Document docu = writeProjectConfig(config);
            pipeIn = new ByteArrayInputStream(XMLUtil.toByteArray(docu));

            // create or overwrite the .checkstyle file
            IProject project = config.getProject();
            IFile file = project.getFile(ProjectConfigurationFactory.PROJECT_CONFIGURATION_FILE);
            if (!file.exists()) {
                file.create(pipeIn, true, null);
                file.refreshLocal(IResource.DEPTH_INFINITE, null);
            } else {
                file.setContents(pipeIn, true, true, null);
            }

            config.getLocalCheckConfigWorkingSet().store();
        } catch (Exception e) {
            CheckstylePluginException.rethrow(e,
                    NLS.bind(Messages.errorWritingCheckConfigurations, e.getLocalizedMessage()));
        } finally {
            IOUtils.closeQuietly(pipeIn);
            IOUtils.closeQuietly(pipeOut);
        }
    }

    /**
     * Produces the sax events to write a project configuration.
     * 
     * @param config
     *            the configuration
     */
    private Document writeProjectConfig(ProjectConfigurationWorkingCopy config) throws CheckstylePluginException {

        Document doc = DocumentHelper.createDocument();

        Element root = doc.addElement(XMLTags.FILESET_CONFIG_TAG);
        root.addAttribute(XMLTags.FORMAT_VERSION_TAG, ProjectConfigurationFactory.CURRENT_FILE_FORMAT_VERSION);
        root.addAttribute(XMLTags.SIMPLE_CONFIG_TAG, Boolean.toString(config.isUseSimpleConfig()));
        root.addAttribute(XMLTags.SYNC_FORMATTER_TAG, Boolean.toString(config.isSyncFormatter()));

        ICheckConfiguration[] workingCopies = config.getLocalCheckConfigWorkingSet().getWorkingCopies();
        for (int i = 0; i < workingCopies.length; i++) {
            writeLocalConfiguration(workingCopies[i], root);
        }

        for (FileSet fileSet : config.getFileSets()) {
            writeFileSet(fileSet, config.getProject(), root);
        }

        // write filters
        for (IFilter filter : config.getFilters()) {
            writeFilter(filter, root);
        }

        return doc;
    }

    /**
     * Writes a local check configuration.
     * 
     * @param checkConfig
     *            the local check configuration
     * @param docRoot
     *            the root element of the project configuration
     */
    private void writeLocalConfiguration(ICheckConfiguration checkConfig, Element docRoot) {

        // TODO refactor to avoid code duplication with
        // GlobalCheckConfigurationWorkingSet

        // don't store built-in configurations to persistence or local
        // configurations
        if (checkConfig.getType() instanceof BuiltInConfigurationType || checkConfig.isGlobal()) {
            return;
        }

        // RFE 1420212
        String location = checkConfig.getLocation();
        if (checkConfig.getType() instanceof ProjectConfigurationType) {
            IProject project = mProjectConfig.getProject();
            IWorkspaceRoot root = project.getWorkspace().getRoot();
            IFile configFile = root.getFile(new Path(location));
            IProject configFileProject = configFile.getProject();

            // if the configuration is in *same* project don't store project
            // path part
            if (project.equals(configFileProject)) {
                location = configFile.getProjectRelativePath().toString();
            }
        }

        Element configEl = docRoot.addElement(XMLTags.CHECK_CONFIG_TAG);
        configEl.addAttribute(XMLTags.NAME_TAG, checkConfig.getName());
        configEl.addAttribute(XMLTags.LOCATION_TAG, location);
        configEl.addAttribute(XMLTags.TYPE_TAG, checkConfig.getType().getInternalName());
        if (checkConfig.getDescription() != null) {
            configEl.addAttribute(XMLTags.DESCRIPTION_TAG, checkConfig.getDescription());
        }

        // Write resolvable properties
        for (ResolvableProperty prop : checkConfig.getResolvableProperties()) {

            Element propEl = configEl.addElement(XMLTags.PROPERTY_TAG);
            propEl.addAttribute(XMLTags.NAME_TAG, prop.getPropertyName());
            propEl.addAttribute(XMLTags.VALUE_TAG, prop.getValue());
        }

        // Write additional data
        for (Map.Entry<String, String> entry : checkConfig.getAdditionalData().entrySet()) {

            Element addEl = configEl.addElement(XMLTags.ADDITIONAL_DATA_TAG);
            addEl.addAttribute(XMLTags.NAME_TAG, entry.getKey());
            addEl.addAttribute(XMLTags.VALUE_TAG, entry.getValue());
        }
    }

    /**
     * Produces the sax events to write a file set to xml.
     * 
     * @param fileSet
     *            the file set
     * @param project
     *            the project
     * @param docRoot
     *            the root element of the project configuration
     */
    private void writeFileSet(FileSet fileSet, IProject project, Element docRoot) throws CheckstylePluginException {

        if (fileSet.getCheckConfig() == null) {
            throw new CheckstylePluginException(
                    NLS.bind(Messages.errorFilesetWithoutCheckConfig, fileSet.getName(), project.getName()));
        }

        Element fileSetEl = docRoot.addElement(XMLTags.FILESET_TAG);
        fileSetEl.addAttribute(XMLTags.NAME_TAG, fileSet.getName());
        fileSetEl.addAttribute(XMLTags.ENABLED_TAG, Boolean.toString(fileSet.isEnabled()));

        ICheckConfiguration checkConfig = fileSet.getCheckConfig();
        if (checkConfig != null) {

            fileSetEl.addAttribute(XMLTags.CHECK_CONFIG_NAME_TAG, checkConfig.getName());
            fileSetEl.addAttribute(XMLTags.LOCAL_TAG, Boolean.toString(!checkConfig.isGlobal()));
        }

        // write patterns
        for (FileMatchPattern pattern : fileSet.getFileMatchPatterns()) {

            Element patternEl = fileSetEl.addElement(XMLTags.FILE_MATCH_PATTERN_TAG);
            patternEl.addAttribute(XMLTags.MATCH_PATTERN_TAG,
                    pattern.getMatchPattern() != null ? pattern.getMatchPattern() : "");
            patternEl.addAttribute(XMLTags.INCLUDE_PATTERN_TAG, Boolean.toString(pattern.isIncludePattern()));
        }
    }

    /**
     * Produces the sax events to write a filter to xml.
     * 
     * @param filter
     *            the filter
     * @param docRoot
     *            the root element of the project configuration
     */
    private void writeFilter(IFilter filter, Element docRoot) {

        // write only filters that are actually changed
        // (enabled or contain data)
        IFilter prototype = PluginFilters.getByInternalName(filter.getInternalName());
        if (prototype.equals(filter)) {
            return;
        }

        Element filterEl = docRoot.addElement(XMLTags.FILTER_TAG);
        filterEl.addAttribute(XMLTags.NAME_TAG, filter.getInternalName());
        filterEl.addAttribute(XMLTags.ENABLED_TAG, Boolean.toString(filter.isEnabled()));

        List<String> data = filter.getFilterData();
        if (data != null && !data.isEmpty()) {
            for (String item : data) {

                Element dataEl = filterEl.addElement(XMLTags.FILTER_DATA_TAG);
                dataEl.addAttribute(XMLTags.VALUE_TAG, item);
            }
        }
    }
}