001package jmri.profile;
002
003import java.io.File;
004import java.io.IOException;
005import java.util.prefs.Preferences;
006import javax.annotation.Nonnull;
007import jmri.util.FileUtil;
008import jmri.util.FileUtilSupport;
009import jmri.util.node.NodeIdentity;
010import jmri.util.prefs.JmriConfigurationProvider;
011import jmri.util.prefs.JmriPreferencesProvider;
012import jmri.util.prefs.JmriUserInterfaceConfigurationProvider;
013import org.slf4j.Logger;
014import org.slf4j.LoggerFactory;
015
016/**
017 * Utility methods to get information about {@link jmri.profile.Profile}s.
018 *
019 * @author Randall Wood 2015
020 */
021public class ProfileUtils {
022
023    private final static Logger log = LoggerFactory.getLogger(ProfileUtils.class);
024
025    /**
026     * Get the XMl configuration container for a given configuration profile.
027     *
028     * @param project The project to get the configuration container for, or
029     *                null to get a configuration container that can apply to
030     *                all projects on this computer
031     * @return An XML configuration container, possibly empty
032     */
033    public static AuxiliaryConfiguration getAuxiliaryConfiguration(Profile project) {
034        return JmriConfigurationProvider.getConfiguration(project);
035    }
036
037    /**
038     * Get the preferences needed by a class for a given configuration profile.
039     *
040     * @param project The project to get the configuration for, or null to get a
041     *                preferences object that can apply to all projects on this
042     *                computer
043     * @param clazz   The class requesting preferences
044     * @param shared  True if the preferences are for all nodes (computers) this
045     *                project may run on, false if the preferences are only for
046     *                this node; ignored if the value of project is null
047     * @return The preferences
048     */
049    public static Preferences getPreferences(Profile project, Class<?> clazz, boolean shared) {
050        return JmriPreferencesProvider.getPreferences(project, clazz, shared);
051    }
052
053    /**
054     * Get the XMl configuration container for a given configuration profile's
055     * user interface state.
056     *
057     * @param project The project to get the configuration container for, or
058     *                null to get a configuration container that can apply to
059     *                all projects on this computer
060     * @return An XML configuration container, possibly empty
061     */
062    public static AuxiliaryConfiguration getUserInterfaceConfiguration(Profile project) {
063        return JmriUserInterfaceConfigurationProvider.getConfiguration(project);
064    }
065
066    /**
067     * Get the local cache directory for the given profile.
068     * <p>
069     * This cache is outside the profile for which the cache exists to prevent
070     * the possibility that different JMRI installations have different contents
071     * that would invalidate the cache if copied from one computer to another.
072     *
073     * @param project the project to get the cache directory for, or null to get
074     *                the cache directory for all projects on this computer
075     * @param owner   The class owning the cached information, or null to get
076     *                the cache directory for the project
077     * @return a directory in which data can be cached
078     */
079    public static File getCacheDirectory(Profile project, Class<?> owner) {
080        File cache = FileUtilSupport.getDefault().getCacheDirectory();
081        if (project != null) {
082            cache = new File(cache, project.getId());
083        }
084        if (owner != null) {
085            cache = new File(cache, JmriPreferencesProvider.findCNBForClass(owner));
086        }
087        FileUtil.createDirectory(cache);
088        return cache;
089    }
090
091    /**
092     * Copy one profile configuration to another profile.
093     *
094     * @param source      The source profile.
095     * @param destination The destination profile.
096     * @throws IllegalArgumentException If the destination profile is the active
097     *                                  profile.
098     * @throws IOException              If the copy cannot be completed.
099     */
100    public static void copy(@Nonnull Profile source, @Nonnull Profile destination) throws IllegalArgumentException, IOException {
101        if (destination.equals(ProfileManager.getDefault().getActiveProfile())) {
102            throw new IllegalArgumentException("Target profile cannot be active profile.");
103        }
104        FileUtil.copy(source.getPath(), destination.getPath());
105        File profile = new File(destination.getPath(), Profile.PROFILE);
106        File[] files = profile.listFiles((File pathname) -> (pathname.getName().endsWith(source.getUniqueId())));
107        if (files != null) {
108            for (File file : files) {
109                if (!file.renameTo(new File(profile, file.getName().replace(source.getUniqueId(), destination.getUniqueId())))) {
110                    throw new IOException("Unable to rename " + file + " to use new profile ID");
111                }
112            }
113        }
114        destination.save();
115    }
116
117    /**
118     * Copy the most recently modified former identity, if any, for the current computer
119     * in the given profile to the current storage identity of the current computer for
120     * the given profile.
121     *
122     * @param profile the profile containing identities to copy
123     * @return true if an existing identity is copied, false otherwise
124     * @throws IOException if unable to a copy an existing identity
125     */
126    public static boolean copyPrivateContentToCurrentIdentity(@Nonnull Profile profile) throws IOException {
127        String uniqueId = "-" + profile.getUniqueId();
128        File newPath = new File(new File(profile.getPath(), Profile.PROFILE), NodeIdentity.storageIdentity(profile));
129        if (!newPath.exists()) {
130            File oldPath = null;
131            for (String identity : NodeIdentity.formerIdentities()) {
132                if (oldPath == null) {
133                    File path = new File(new File(profile.getPath(), Profile.PROFILE), identity + uniqueId);
134                    if (path.exists()) {
135                        oldPath = path;
136                    }
137                } else {
138                    File path = new File(new File(profile.getPath(), Profile.PROFILE), identity + uniqueId);
139                    if (path.exists() && path.lastModified() > oldPath.lastModified()) {
140                        oldPath = path;
141                    }
142                }
143            }
144            if (oldPath != null && oldPath.exists()) {
145                try {
146                    log.info("Copying from old node \"{}\" to new node \"{}\"", oldPath, newPath);
147                    FileUtil.copy(oldPath, newPath);
148                    return true;
149                } catch (IOException ex) {
150                    log.warn("Failed copying \"{}\" to \"{}\"", oldPath, newPath);
151                }
152            }
153        }
154        return false;
155    }
156}