001/*
002 * This file is part of McIDAS-V
003 *
004 * Copyright 2007-2016
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     * @deprecated There's not really a need for this method; the JVM can
162     *             tell us the amount of memory.
163     */
164    public void setAvailableMemory(String megabytes) {
165        Objects.requireNonNull(megabytes, "Available memory cannot be null");
166        if (megabytes.isEmpty()) {
167            megabytes = "0";
168        }
169        
170        try {
171            int test = Integer.parseInt(megabytes);
172            if (test < 0) {
173                throw new IllegalArgumentException("Available memory must be a non-negative integer, not \""+megabytes+"\"");
174            }
175            availableMemory = test;
176        } catch (NumberFormatException e) {
177            throw new IllegalArgumentException("Could not convert \""+megabytes+"\" to a non-negative integer", e);
178        }
179    }
180    
181    /**
182     * Returns the path to the user's {@literal "userpath"} directory.
183     * 
184     * @return Path to the user's directory.
185     */
186    public String getUserDirectory() {
187        return userDirectory;
188    }
189    
190    /**
191     * Returns the path to a file in the user's {@literal "userpath"} directory.
192     * 
193     * @param filename Filename within the {@code userpath}. Cannot be 
194     * {@code null}, but does not need to be a filename that already exists 
195     * within the {@code userpath}.
196     * 
197     * @return Path to a file in the user's directory. <b>Note:</b> the file 
198     * may not yet exist.
199     */
200    public String getUserFile(String filename) {
201        return Paths.get(userDirectory, filename).toString();
202    }
203
204    /**
205     * Returns the path to the user's bundles directory. Note: this should be
206     * a directory within {@link #getUserDirectory()}.
207     *
208     * @return Path to the user's bundles directory.
209     */
210    public String getUserBundles() {
211        return userBundles;
212    }
213    
214    /**
215     * Returns the amount of available memory in megabytes.
216     * 
217     * @return Available memory in megabytes.
218     */
219    public int getAvailableMemory() {
220        return availableMemory;
221    }
222    
223    /**
224     * Returns the path of user's copy of the startup preferences.
225     * 
226     * @return Path to the user's startup preferences file.
227     */
228    public String getUserPrefs() {
229        return userPrefs;
230    }
231    
232    /**
233     * Returns the path of the startup preferences included in the McIDAS-V
234     * distribution. Mostly useful for normalizing the user directory.
235     * 
236     * @return Path to the default startup preferences.
237     * 
238     * @see OptionMaster#normalizeUserDirectory()
239     */
240    public String getDefaultPrefs() {
241        return defaultPrefs;
242    }
243    
244    /**
245     * Returns the platform's notion of a new line.
246     * 
247     * @return Unix-like: {@literal \n}; Windows: {@literal \r\n}.
248     */
249    public String getNewLine() {
250        return newLine;
251    }
252    
253    /**
254     * Returns a brief summary of the platform specific file locations. 
255     * Please note that the format and contents are subject to change.
256     * 
257     * @return String that looks like 
258     * {@code [Platform@HASHCODE: defaultPrefs=..., userDirectory=..., 
259     * userPrefs=...]}
260     */
261    @Override public String toString() {
262        return String.format(
263            "[Platform@%x: defaultPrefs=%s, userDirectory=%s, userPrefs=%s]",
264            hashCode(), defaultPrefs, userDirectory, userPrefs);
265    }
266}