001/*
002 * This file is part of McIDAS-V
003 *
004 * Copyright 2007-2015
005 * Space Science and Engineering Center (SSEC)
006 * University of Wisconsin - Madison
007 * 1225 W. Dayton Street, Madison, WI 53706, USA
008 * https://www.ssec.wisc.edu/mcidas
009 * 
010 * All Rights Reserved
011 * 
012 * McIDAS-V is built on Unidata's IDV and SSEC's VisAD libraries, and
013 * some McIDAS-V source code is based on IDV and VisAD source code.  
014 * 
015 * McIDAS-V is free software; you can redistribute it and/or modify
016 * it under the terms of the GNU Lesser Public License as published by
017 * the Free Software Foundation; either version 3 of the License, or
018 * (at your option) any later version.
019 * 
020 * McIDAS-V is distributed in the hope that it will be useful,
021 * but WITHOUT ANY WARRANTY; without even the implied warranty of
022 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
023 * GNU Lesser Public License for more details.
024 * 
025 * You should have received a copy of the GNU Lesser Public License
026 * along with this program.  If not, see http://www.gnu.org/licenses.
027 */
028package edu.wisc.ssec.mcidasv.startupmanager;
029
030import java.io.File;
031import java.nio.file.Files;
032import java.nio.file.Path;
033import java.nio.file.Paths;
034import java.util.Objects;
035
036import edu.wisc.ssec.mcidasv.Constants;
037import edu.wisc.ssec.mcidasv.startupmanager.options.OptionMaster;
038
039/**
040 * Represents platform specific details used by McIDAS-V. In particular, there
041 * are useful methods related to the McIDAS-V {@literal "userpath"}.
042 *
043 * <p>Currently McIDAS-V distinguishes between {@literal "Unix-like"} and
044 * {@literal "Windows"}; these can be accessed using {@code Platform.UNIXLIKE}
045 * or {@code Platform.WINDOWS}.</p>
046 */
047public enum Platform {
048    /** Instance of unix-specific platform information. */
049    UNIXLIKE("runMcV.prefs", "\n"),
050    
051    /** Instance of windows-specific platform information. */
052    WINDOWS("runMcV-Prefs.bat", "\r\n");
053    
054    /** Path to the user's {@literal "userpath"} directory. */
055    private String userDirectory;
056    
057    /** The path to the user's copy of the startup preferences. */
058    private String userPrefs;
059    
060    /** Path to the preference file that ships with McIDAS-V. */
061    private final String defaultPrefs;
062    
063    /** Holds the platform's representation of a new line. */
064    private final String newLine;
065
066    /** Path to the bundles subdirectory within {@code userDirectory}. */
067    private final String userBundles;
068    
069    /** Total amount of memory avilable in megabytes */
070    private int availableMemory = 0;
071    
072    /**
073     * Initializes the platform-specific paths to the different files 
074     * required by the startup manager.
075     * 
076     * @param defaultPrefs Path to the preferences file that ships with
077     * McIDAS-V. Cannot be {@code null} or empty.
078     * @param newLine Character(s!) that represent a new line for this 
079     * platform. Cannot be {@code null} or empty.
080     * 
081     * @throws NullPointerException if either {@code defaultPrefs} or
082     * {@code newLine} are {@code null}.
083     * 
084     * @throws IllegalArgumentException if either {@code defaultPrefs} or
085     * {@code newLine} are an empty string.
086     */
087    Platform(final String defaultPrefs, final String newLine) {
088        Objects.requireNonNull(defaultPrefs);
089        Objects.requireNonNull(newLine);
090        if (defaultPrefs.isEmpty() || newLine.isEmpty()) {
091            throw new IllegalArgumentException("");
092        }
093
094        String osName = System.getProperty("os.name");
095        Path tmpPath;
096        if (osName.startsWith("Mac OS X")) {
097            tmpPath = Paths.get(System.getProperty("user.home"), "Documents", Constants.USER_DIRECTORY_NAME);
098        } else {
099            tmpPath = Paths.get(System.getProperty("user.home"), Constants.USER_DIRECTORY_NAME);
100        }
101
102        this.userDirectory = tmpPath.toString();
103        this.userPrefs = Paths.get(userDirectory, defaultPrefs).toString();
104        this.defaultPrefs = defaultPrefs;
105        this.newLine = newLine;
106        this.userBundles = Paths.get(this.userDirectory, "bundles").toString();
107    }
108    
109    /**
110     * Sets the path to the user's {@literal "userpath"} directory explicitly.
111     * If the specified path does not yet exist, this method will first
112     * attempt to create it. The method will then attempt to verify whether or
113     * not McIDAS-V can use the path.
114     * 
115     * @param path New path. Cannot be {@code null}, but does not have to exist
116     * prior to running this method. Be aware that this method will attempt to
117     * create {@code path} if it does not already exist.
118     *
119     * @throws IllegalArgumentException if {@code path} is not a
120     * directory, or if it not both readable and writable.
121     */
122    public void setUserDirectory(final String path) throws IllegalArgumentException {
123        File tmp = new File(path);
124        if (!tmp.exists()) {
125            tmp.mkdir();
126        }
127
128        // TODO(jon): or would tmp.isFile() suffice?
129        if (tmp.exists() && !tmp.isDirectory()) {
130            throw new IllegalArgumentException('\'' +path+"' is not a directory.");
131        }
132
133        Path p = tmp.toPath();
134        boolean canRead = Files.isReadable(p);
135        boolean canWrite = Files.isWritable(p);
136
137        if (!canRead && !canWrite) {
138            throw new IllegalArgumentException('\''+path+"' must be both readable and writable by McIDAS-V.");
139        } else if (!canRead) {
140            throw new IllegalArgumentException('\''+path+"' must be readable by McIDAS-V.");
141        } else if (!canWrite) {
142            throw new IllegalArgumentException('\''+path+"' must be writable by McIDAS-V.");
143        }
144
145        userDirectory = path;
146        userPrefs = Paths.get(userDirectory, defaultPrefs).toString();
147    }
148    
149    /**
150     * Sets the amount of available memory. {@code megabytes} must be 
151     * greater than or equal to zero.
152     * 
153     * @param megabytes Memory in megabytes.
154     * 
155     * @throws NullPointerException if {@code megabytes} is {@code null}.
156     * @throws IllegalArgumentException if {@code megabytes} is less than
157     * zero or does not represent an integer.
158     * 
159     * @see StartupManager#getArgs
160     */
161    public void setAvailableMemory(String megabytes) {
162        Objects.requireNonNull(megabytes, "Available memory cannot be null");
163        if (megabytes.isEmpty()) {
164            megabytes = "0";
165        }
166        
167        try {
168            int test = Integer.parseInt(megabytes);
169            if (test < 0) {
170                throw new IllegalArgumentException("Available memory must be a non-negative integer, not \""+megabytes+"\"");
171            }
172            availableMemory = test;
173        } catch (NumberFormatException e) {
174            throw new IllegalArgumentException("Could not convert \""+megabytes+"\" to a non-negative integer");
175        }
176    }
177    
178    /**
179     * Returns the path to the user's {@literal "userpath"} directory.
180     * 
181     * @return Path to the user's directory.
182     */
183    public String getUserDirectory() {
184        return userDirectory;
185    }
186    
187    /**
188     * Returns the path to a file in the user's {@literal "userpath"} directory.
189     * 
190     * @param filename Filename within the {@code userpath}. Cannot be 
191     * {@code null}, but does not need to be a filename that already exists 
192     * within the {@code userpath}.
193     * 
194     * @return Path to a file in the user's directory. <b>Note:</b> the file 
195     * may not yet exist.
196     */
197    public String getUserFile(String filename) {
198        return Paths.get(userDirectory, filename).toString();
199    }
200
201    /**
202     * Returns the path to the user's bundles directory. Note: this should be
203     * a directory within {@link #getUserDirectory()}.
204     *
205     * @return Path to the user's bundles directory.
206     */
207    public String getUserBundles() {
208        return userBundles;
209    }
210    
211    /**
212     * Returns the amount of available memory in megabytes.
213     * 
214     * @return Available memory in megabytes.
215     */
216    public int getAvailableMemory() {
217        return availableMemory;
218    }
219    
220    /**
221     * Returns the path of user's copy of the startup preferences.
222     * 
223     * @return Path to the user's startup preferences file.
224     */
225    public String getUserPrefs() {
226        return userPrefs;
227    }
228    
229    /**
230     * Returns the path of the startup preferences included in the McIDAS-V
231     * distribution. Mostly useful for normalizing the user directory.
232     * 
233     * @return Path to the default startup preferences.
234     * 
235     * @see OptionMaster#normalizeUserDirectory()
236     */
237    public String getDefaultPrefs() {
238        return defaultPrefs;
239    }
240    
241    /**
242     * Returns the platform's notion of a new line.
243     * 
244     * @return Unix-like: {@literal \n}; Windows: {@literal \r\n}.
245     */
246    public String getNewLine() {
247        return newLine;
248    }
249    
250    /**
251     * Returns a brief summary of the platform specific file locations. 
252     * Please note that the format and contents are subject to change.
253     * 
254     * @return String that looks like 
255     * {@code [Platform@HASHCODE: defaultPrefs=..., userDirectory=..., 
256     * userPrefs=...]}
257     */
258    @Override public String toString() {
259        return String.format(
260            "[Platform@%x: defaultPrefs=%s, userDirectory=%s, userPrefs=%s]",
261            hashCode(), defaultPrefs, userDirectory, userPrefs);
262    }
263}