Package org.netbeans.gradle.project.properties

Source Code of org.netbeans.gradle.project.properties.ProjectPropertiesManager$CachedProperties

package org.netbeans.gradle.project.properties;

import java.io.File;
import java.nio.charset.Charset;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Set;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicReference;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener;
import org.jtrim.cancel.Cancellation;
import org.jtrim.cancel.CancellationToken;
import org.jtrim.concurrent.CancelableTask;
import org.jtrim.concurrent.WaitableSignal;
import org.jtrim.utils.ExceptionHelper;
import org.netbeans.api.java.platform.JavaPlatform;
import org.netbeans.gradle.project.NbGradleProject;
import org.netbeans.gradle.project.api.config.ProfileDef;
import org.netbeans.gradle.project.api.entry.ProjectPlatform;
import org.netbeans.gradle.project.persistent.PropertiesPersister;
import org.netbeans.gradle.project.persistent.XmlPropertiesPersister;

public final class ProjectPropertiesManager {
    private static final Logger LOGGER = Logger.getLogger(ProjectPropertiesManager.class.getName());

    private static final Lock MAIN_LOCK = new ReentrantLock();
    // Don't forget that the value can't be wrapped, it must be the one to be
    // returned, otherwise it might get garbage collected.
    private static final Map<ProjectPropertySource, CachedProperties> PROPERTIES
            = new WeakValueHashMap<>();

    private static void saveIfRequired(
            final NbGradleProject project,
            final AtomicBoolean saveQueued,
            final ProjectProperties properties,
            final PropertiesPersister persister) {

        if (saveQueued.compareAndSet(false, true)) {
            PropertiesPersister.PERSISTER_PROCESSOR.execute(Cancellation.UNCANCELABLE_TOKEN, new CancelableTask() {
                @Override
                public void execute(CancellationToken cancelToken) {
                    saveQueued.set(false);
                    persister.save(project, properties, null);
                }
            }, null);
        }
    }

    private static void setSaveOnChange(
            final NbGradleProject project,
            final ProjectProperties properties,
            final PropertiesPersister persister) {

        final AtomicBoolean saveQueued = new AtomicBoolean(false);

        ChangeListener saveIfRequiredTask = new ChangeListener() {
            @Override
            public void stateChanged(ChangeEvent e) {
                saveIfRequired(project, saveQueued, properties, persister);
            }
        };

        for (MutableProperty<?> property: properties.getAllProperties()) {
            property.addChangeListener(saveIfRequiredTask);
        }
    }

    public static ProjectProperties getProperties(
            NbGradleProject project,
            final WaitableSignal loadedSignal) {
        ExceptionHelper.checkNotNullArgument(loadedSignal, "loadedSignal");
        return getPropertySourceForProject(project).load(new PropertiesLoadListener() {
            @Override
            public void loadedProperties(ProjectProperties properties) {
                loadedSignal.signal();
            }
        });
    }

    public static ProjectPropertySource getPropertySourceForProject(
            NbGradleProject project, ProfileDef profileDef) {
        return new NbProfileProjectPropertySource(project, profileDef);
    }

    public static ProjectPropertySource getPropertySourceForProject(NbGradleProject project) {
        return new NbCurrentProfileProjectPropertySource(project);
    }

    public static ProjectPropertySource getFilesPropertySource(NbGradleProject project, File... propertiesFiles) {
        return getFilesPropertySource(project, Arrays.asList(propertiesFiles));
    }

    public static ProjectPropertySource getFilesPropertySource(NbGradleProject project, List<File> propertiesFiles) {
        List<ProjectPropertySource> sources = new ArrayList<>();
        for (File propertyFile: propertiesFiles) {
            sources.add(getFilePropertySource(project, propertyFile));
        }
        return combineSources(sources);
    }

    public static ProjectPropertySource getFilePropertySource(NbGradleProject project, final File propertiesFile) {
        return new FileProjectPropertySource(project, propertiesFile);
    }

    public static ProjectPropertySource combineSources(List<? extends ProjectPropertySource> propertySources) {
        return combineSources(propertySources.toArray(new ProjectPropertySource[propertySources.size()]), 0);
    }

    public static ProjectPropertySource combineSources(ProjectPropertySource... propertySources) {
        return combineSources(propertySources, 0);
    }

    private static ProjectPropertySource combineSources(
            final ProjectPropertySource[] propertySources,
            final int offset) {

        return new CombinedProjectPropertySource(propertySources, offset);
    }

    private static CachedProperties loadPropertiesAlways(
            final NbGradleProject project,
            File propertiesFile) {

        final CachedProperties result = new CachedProperties(new MemProjectProperties());
        final PropertiesPersister persister = new XmlPropertiesPersister(propertiesFile);
        PropertiesPersister.PERSISTER_PROCESSOR.execute(Cancellation.UNCANCELABLE_TOKEN, new CancelableTask() {
                @Override
                public void execute(CancellationToken cancelToken) {
                persister.load(result, false, new Runnable() {
                    @Override
                    public void run() {
                        try {
                            result.signalPropertiesLoaded();
                        } finally {
                            setSaveOnChange(project, result, persister);
                        }
                    }
                });
            }
        }, null);
        return result;
    }

    private static class FileProjectPropertySource implements ProjectPropertySource {
        private final NbGradleProject project;
        private final File propertiesFile;

        public FileProjectPropertySource(NbGradleProject project, File propertiesFile) {
            ExceptionHelper.checkNotNullArgument(propertiesFile, "propertiesFile");
            this.project = project;
            this.propertiesFile = propertiesFile;
        }

        @Override
        public ProjectProperties load(PropertiesLoadListener onLoadTask) {
            CachedProperties result;
            MAIN_LOCK.lock();
            try {
                result = PROPERTIES.get(this);
            } finally {
                MAIN_LOCK.unlock();
            }

            if (result == null) {
                final CachedProperties newProperties = loadPropertiesAlways(project, propertiesFile);

                MAIN_LOCK.lock();
                try {
                    result = PROPERTIES.get(this);
                    if (result == null) {
                        PROPERTIES.put(this, newProperties);
                        result = newProperties;
                    }
                } finally {
                    MAIN_LOCK.unlock();
                }
            }

            assert result != null;

            if (onLoadTask != null) {
                result.notifyOnLoad(onLoadTask);
            }

            return result;
        }

        @Override
        public int hashCode() {
            return 371 + propertiesFile.hashCode();
        }

        @Override
        public boolean equals(Object obj) {
            if (obj == null) return false;
            if (obj == this) return true;
            if (getClass() != obj.getClass()) return false;

            final FileProjectPropertySource other = (FileProjectPropertySource)obj;
            return this.propertiesFile == other.propertiesFile
                    || this.propertiesFile.equals(other.propertiesFile);
        }
    }

    private static class CombinedProjectPropertySource implements ProjectPropertySource {
        private final ProjectPropertySource[] propertySources;

        public CombinedProjectPropertySource(ProjectPropertySource[] propertySources, int offset) {
            this.propertySources = Arrays.copyOfRange(propertySources, offset, propertySources.length);

            ExceptionHelper.checkNotNullElements(this.propertySources, "propertySources");
        }

        @Override
        public ProjectProperties load(final PropertiesLoadListener onLoadTask) {
            CachedProperties result;
            MAIN_LOCK.lock();
            try {
                result = PROPERTIES.get(this);
            } finally {
                MAIN_LOCK.unlock();
            }

            if (result == null) {
                final AtomicReference<CachedProperties> resultRef
                        = new AtomicReference<>(null);

                final AtomicInteger subTaskCount = new AtomicInteger();
                // Setting the value of resultRef is counted as a subTask as well.
                // This resultForwarder will call onLoadTask only after all
                // properties have been loaded.
                final PropertiesLoadListener resultForwarder = new PropertiesLoadListener() {
                    @Override
                    public void loadedProperties(ProjectProperties properties) {
                        if (subTaskCount.decrementAndGet() == 0 && onLoadTask != null) {
                            CachedProperties loadedProperties = resultRef.get();
                            if (loadedProperties == null) {
                                String message = "Internal error while loading properties.";
                                LOGGER.log(Level.SEVERE, message, new IllegalStateException(message));
                                return;
                            }
                            loadedProperties.signalPropertiesLoaded();
                        }
                    }
                };

                ProjectProperties newProperties;
                if (propertySources.length == 1) {
                    subTaskCount.set(2);
                    newProperties = propertySources[0].load(onLoadTask);
                }
                else {
                    subTaskCount.set(3);

                    ProjectProperties mainProperties = propertySources[0].load(resultForwarder);
                    ProjectProperties fallbackProperties
                            = combineSources(propertySources, 1).load(resultForwarder);

                    newProperties = new FallbackProjectProperties(mainProperties, fallbackProperties);
                }

                CachedProperties cachedNewProperties = new CachedProperties(newProperties);
                MAIN_LOCK.lock();
                try {
                    result = PROPERTIES.get(this);
                    if (result == null) {
                        PROPERTIES.put(this, cachedNewProperties);
                        result = cachedNewProperties;
                    }
                } finally {
                    MAIN_LOCK.unlock();
                }

                resultRef.set(result);
                resultForwarder.loadedProperties(result);
            }

            if (onLoadTask != null) {
                result.notifyOnLoad(onLoadTask);
            }

            return result;
        }

        @Override
        public int hashCode() {
            return 581 + Arrays.hashCode(this.propertySources);
        }

        @Override
        public boolean equals(Object obj) {
            if (obj == null) return false;
            if (obj == this) return true;
            if (getClass() != obj.getClass()) return false;

            final CombinedProjectPropertySource other = (CombinedProjectPropertySource)obj;
            return Arrays.equals(this.propertySources, other.propertySources);
        }
    }

    private static ProjectPropertySource filesWithDefault(
            NbGradleProject project, File[] files, ProjectPropertySource defaultSource) {

        ProjectPropertySource[] sources = new ProjectPropertySource[files.length + 1];
        for (int i = 0; i < files.length; i++) {
            sources[i] = getFilePropertySource(project, files[i]);
        }
        sources[files.length] = defaultSource;

        return combineSources(sources);
    }

    private static final class NbProfileProjectPropertySource implements ProjectPropertySource {
        private final NbGradleProject project;
        private final ProfileDef profileDef;
        private final ProjectPropertySource defaultSource;

        public NbProfileProjectPropertySource(NbGradleProject project, ProfileDef profileDef) {
            ExceptionHelper.checkNotNullArgument(project, "project");

            this.project = project;
            this.profileDef = profileDef;
            this.defaultSource = new DefaultProjectPropertySource(project);
        }

        @Override
        public ProjectProperties load(final PropertiesLoadListener onLoadTask) {
            File[] files = SettingsFiles.getFilesForProfile(project, profileDef);
            return filesWithDefault(project, files, defaultSource).load(onLoadTask);
        }

        @Override
        public int hashCode() {
            int hash = 5;
            hash = 73 * hash + project.hashCode();
            hash = 73 * hash + Objects.hashCode(profileDef);
            return hash;
        }

        @Override
        public boolean equals(Object obj) {
            if (obj == null) return false;
            if (obj == this) return true;
            if (getClass() != obj.getClass()) return false;

            final NbProfileProjectPropertySource other = (NbProfileProjectPropertySource)obj;

            return Objects.equals(this.project, other.project)
                    && Objects.equals(this.profileDef, other.profileDef);
        }
    }

    private static final class NbCurrentProfileProjectPropertySource implements ProjectPropertySource {
        private final NbGradleProject project;
        private final ProjectPropertySource defaultSource;

        public NbCurrentProfileProjectPropertySource(NbGradleProject project) {
            ExceptionHelper.checkNotNullArgument(project, "project");

            this.project = project;
            this.defaultSource = new DefaultProjectPropertySource(project);
        }

        @Override
        public ProjectProperties load(final PropertiesLoadListener onLoadTask) {
            File[] files = SettingsFiles.getFilesForProject(project);
            return filesWithDefault(project, files, defaultSource).load(onLoadTask);
        }

        @Override
        public int hashCode() {
            return 335 + this.project.hashCode();
        }

        @Override
        public boolean equals(Object obj) {
            if (obj == null) return false;
            if (obj == this) return true;
            if (getClass() != obj.getClass()) return false;

            final NbCurrentProfileProjectPropertySource other = (NbCurrentProfileProjectPropertySource)obj;
            return this.project.equals(other.project);
        }
    }

    private static final class CachedProperties implements ProjectProperties {
        private final ProjectProperties properties;
        private final Lock loadLock;
        private final List<PropertiesLoadListener> onLoadedTask;
        private volatile boolean loaded;

        public CachedProperties(ProjectProperties properties) {
            assert properties != null;
            this.properties = properties;
            this.loadLock = new ReentrantLock();
            this.onLoadedTask = new LinkedList<>();
        }

        public void signalPropertiesLoaded() {
            PropertiesLoadListener[] toNotify;
            loadLock.lock();
            try {
                loaded = true;
                if (onLoadedTask.isEmpty()) {
                    return;
                }

                toNotify = onLoadedTask.toArray(new PropertiesLoadListener[onLoadedTask.size()]);
                onLoadedTask.clear();
            } finally {
                loadLock.unlock();
            }

            for (PropertiesLoadListener listener: toNotify) {
                listener.loadedProperties(properties);
            }
        }

        public void notifyOnLoad(PropertiesLoadListener listener) {
            ExceptionHelper.checkNotNullArgument(listener, "listener");

            if (loaded) {
                listener.loadedProperties(properties);
                return;
            }

            loadLock.lock();
            try {
                if (!loaded) {
                    onLoadedTask.add(listener);
                    return;
                }
            } finally {
                loadLock.unlock();
            }

            listener.loadedProperties(properties);
        }

        @Override
        public MutableProperty<String> getSourceLevel() {
            return properties.getSourceLevel();
        }

        @Override
        public MutableProperty<ProjectPlatform> getPlatform() {
            return properties.getPlatform();
        }

        @Override
        public MutableProperty<JavaPlatform> getScriptPlatform() {
            return properties.getScriptPlatform();
        }

        @Override
        public MutableProperty<GradleLocation> getGradleLocation() {
            return properties.getGradleLocation();
        }

        @Override
        public MutableProperty<Charset> getSourceEncoding() {
            return properties.getSourceEncoding();
        }

        @Override
        public MutableProperty<List<PredefinedTask>> getCommonTasks() {
            return properties.getCommonTasks();
        }

        @Override
        public MutableProperty<LicenseHeaderInfo> getLicenseHeader() {
            return properties.getLicenseHeader();
        }

        @Override
        public MutableProperty<PredefinedTask> tryGetBuiltInTask(String command) {
            return properties.tryGetBuiltInTask(command);
        }

        @Override
        public Set<String> getKnownBuiltInCommands() {
            return properties.getKnownBuiltInCommands();
        }

        @Override
        public MutableProperty<Void> getAuxConfigListener() {
            return properties.getAuxConfigListener();
        }

        @Override
        public AuxConfigProperty getAuxConfig(String elementName, String namespace) {
            return properties.getAuxConfig(elementName, namespace);
        }

        @Override
        public void setAllAuxConfigs(Collection<AuxConfig> configs) {
            properties.setAllAuxConfigs(configs);
        }

        @Override
        public Collection<AuxConfigProperty> getAllAuxConfigs() {
            return properties.getAllAuxConfigs();
        }

        @Override
        public Collection<MutableProperty<?>> getAllProperties() {
            return properties.getAllProperties();
        }
    }

    private ProjectPropertiesManager() {
        throw new AssertionError();
    }
}
TOP

Related Classes of org.netbeans.gradle.project.properties.ProjectPropertiesManager$CachedProperties

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.