001 /*
002 * $Id: OptionMaster.java,v 1.16 2012/02/19 17:35:49 davep Exp $
003 *
004 * This file is part of McIDAS-V
005 *
006 * Copyright 2007-2012
007 * Space Science and Engineering Center (SSEC)
008 * University of Wisconsin - Madison
009 * 1225 W. Dayton Street, Madison, WI 53706, USA
010 * https://www.ssec.wisc.edu/mcidas
011 *
012 * All Rights Reserved
013 *
014 * McIDAS-V is built on Unidata's IDV and SSEC's VisAD libraries, and
015 * some McIDAS-V source code is based on IDV and VisAD source code.
016 *
017 * McIDAS-V is free software; you can redistribute it and/or modify
018 * it under the terms of the GNU Lesser Public License as published by
019 * the Free Software Foundation; either version 3 of the License, or
020 * (at your option) any later version.
021 *
022 * McIDAS-V is distributed in the hope that it will be useful,
023 * but WITHOUT ANY WARRANTY; without even the implied warranty of
024 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
025 * GNU Lesser Public License for more details.
026 *
027 * You should have received a copy of the GNU Lesser Public License
028 * along with this program. If not, see http://www.gnu.org/licenses.
029 */
030
031 package edu.wisc.ssec.mcidasv.startupmanager.options;
032
033 import java.io.BufferedReader;
034 import java.io.BufferedWriter;
035 import java.io.File;
036 import java.io.FileReader;
037 import java.io.FileWriter;
038 import java.io.IOException;
039 import java.util.ArrayList;
040 import java.util.Collection;
041 import java.util.Collections;
042 import java.util.HashMap;
043 import java.util.Map;
044 import java.util.Set;
045
046 import edu.wisc.ssec.mcidasv.startupmanager.StartupManager;
047 import edu.wisc.ssec.mcidasv.startupmanager.StartupManager.Platform;
048
049 public enum OptionMaster {
050 /** The lone OptionMaster instance. */
051 INSTANCE;
052
053 // TODO(jon): write CollectionHelpers.zip() and CollectionHelpers.zipWith()
054 public final Object[][] blahblah = {
055 { "HEAP_SIZE", "Memory", "512m", Type.MEMORY, OptionPlatform.ALL, Visibility.VISIBLE },
056 { "JOGL_TOGL", "Enable JOGL", "1", Type.BOOLEAN, OptionPlatform.UNIXLIKE, Visibility.VISIBLE },
057 { "USE_3DSTUFF", "Enable 3D controls", "1", Type.BOOLEAN, OptionPlatform.ALL, Visibility.VISIBLE },
058 { "DEFAULT_LAYOUT", "Load default layout", "1", Type.BOOLEAN, OptionPlatform.ALL, Visibility.VISIBLE },
059 { "STARTUP_BUNDLE", "Defaults", "", Type.DIRTREE, OptionPlatform.ALL, Visibility.VISIBLE },
060 /**
061 * SLIDER_TEST seems unnecessary...?
062 */
063 // { "SLIDER_TEST", "Slider Test", "50P", Type.SLIDER, OptionPlatform.ALL, Visibility.VISIBLE },
064 /**
065 * TODO: DAVEP: TomW's windows machine needs SET D3DREND= to work properly.
066 * Not sure why, but it shouldn't hurt other users. Investigate after Alpha10
067 */
068 { "D3DREND", "Enable Direct3D", "", Type.BOOLEAN, OptionPlatform.WINDOWS, Visibility.VISIBLE },
069 // mcidasv enables this (the actual property is "visad.java3d.geometryByRef")
070 // by default in mcidasv.properties.
071 { "USE_GEOBYREF", "Enable access to geometry by reference", "1", Type.BOOLEAN, OptionPlatform.ALL, Visibility.VISIBLE },
072 { "USE_IMAGEBYREF", "Enable access to image data by reference", "1", Type.BOOLEAN, OptionPlatform.ALL, Visibility.VISIBLE },
073 { "USE_NPOT", "Enable Non-Power of Two (NPOT) textures", "0", Type.BOOLEAN, OptionPlatform.ALL, Visibility.VISIBLE },
074 // temp bandaid for people suffering from permgen problems.
075 { "USE_CMSGC", "Enable concurrent mark-sweep garbage collector", "0", Type.BOOLEAN, OptionPlatform.ALL, Visibility.VISIBLE },
076 };
077
078 /**
079 * {@link Option}s can be either platform-specific or applicable to all
080 * platforms. Options that are platform-specific still appear in the
081 * UI, but their component is not enabled.
082 */
083 public enum OptionPlatform { ALL, UNIXLIKE, WINDOWS };
084
085 /**
086 * The different types of {@link Option}s.
087 * @see TextOption
088 * @see BooleanOption
089 * @see MemoryOption
090 */
091 public enum Type { TEXT, BOOLEAN, MEMORY, DIRTREE, SLIDER };
092
093 /**
094 * Different ways that an {@link Option} might be displayed.
095 */
096 public enum Visibility { VISIBLE, HIDDEN };
097
098 /** Maps an option ID to the corresponding object. */
099 private final Map<String, Option> optionMap;
100
101 OptionMaster() {
102 normalizeUserDirectory();
103 optionMap = buildOptions(blahblah);
104 // readStartup();
105 }
106
107 /**
108 * Creates the specified options and returns a mapping of the option ID
109 * to the actual {@link Option} object.
110 *
111 * @param options An array specifying the {@code Option}s to be built.
112 *
113 * @return Mapping of ID to {@code Option}.
114 *
115 * @throws AssertionError if the option array contained an entry that
116 * this method cannot build.
117 */
118 private Map<String, Option> buildOptions(final Object[][] options) {
119 // TODO(jon): seriously, get that zip stuff working! this array
120 // stuff is BAD.
121 Map<String, Option> optMap = new HashMap<String, Option>();
122
123 for (Object[] arrayOption : options) {
124 String id = (String)arrayOption[0];
125 String label = (String)arrayOption[1];
126 String defaultValue = (String)arrayOption[2];
127 Type type = (Type)arrayOption[3];
128 OptionPlatform platform = (OptionPlatform)arrayOption[4];
129 Visibility visibility = (Visibility)arrayOption[5];
130
131 Option newOption;
132 switch (type) {
133 case TEXT:
134 newOption = new TextOption(id, label, defaultValue,
135 platform, visibility);
136 break;
137 case BOOLEAN:
138 newOption = new BooleanOption(id, label, defaultValue,
139 platform, visibility);
140 break;
141 case MEMORY:
142 newOption = new MemoryOption(id, label, defaultValue,
143 platform, visibility);
144 break;
145 case DIRTREE:
146 newOption = new DirectoryOption(id, label, defaultValue, platform, visibility);
147 break;
148 case SLIDER:
149 newOption = new SliderOption(id, label, defaultValue, platform, visibility);
150 break;
151 default:
152 throw new AssertionError(type +
153 " is not known to OptionMaster.buildOptions()");
154 }
155 optMap.put(id, newOption);
156 }
157 return optMap;
158 }
159
160 /**
161 * Converts a {@link Platform} to its corresponding
162 * {@link OptionPlatform} type.
163 *
164 * @return The current platform as a {@code OptionPlatform} type.
165 *
166 * @throws AssertionError if {@link StartupManager#getPlatform()}
167 * returned something that this method cannot convert.
168 */
169 // a lame-o hack :(
170 protected OptionPlatform convertToOptionPlatform() {
171 Platform platform = StartupManager.INSTANCE.getPlatform();
172 switch (platform) {
173 case WINDOWS: return OptionPlatform.WINDOWS;
174 case UNIXLIKE: return OptionPlatform.UNIXLIKE;
175 default:
176 throw new AssertionError("Unknown platform: " + platform);
177 }
178 }
179
180 /**
181 * Returns the {@link Option} mapped to {@code id}.
182 *
183 * @param id The ID whose associated {@code Option} is to be returned.
184 *
185 * @return Either the {@code Option} associated with {@code id}, or
186 * {@code null} if there was no association.
187 */
188 public Option getOption(final String id) {
189 return optionMap.get(id);
190 }
191
192 // TODO(jon): getAllOptions and optionsBy* really need some work.
193 // I want to eventually do something like:
194 // Collection<Option> = getOpts().byPlatform(WINDOWS, ALL).byType(BOOLEAN).byVis(HIDDEN)
195 public Collection<Option> getAllOptions() {
196 return Collections.unmodifiableCollection(optionMap.values());
197 }
198
199 public Collection<Option> optionsByPlatform(
200 final Set<OptionPlatform> platforms)
201 {
202 if (platforms == null)
203 throw new NullPointerException();
204
205 Collection<Option> allOptions = getAllOptions();
206 Collection<Option> filteredOptions =
207 new ArrayList<Option>(allOptions.size());
208 for (Option option : allOptions)
209 if (platforms.contains(option.getOptionPlatform()))
210 filteredOptions.add(option);
211 // return Collections.unmodifiableCollection(filteredOptions);
212 return filteredOptions;
213 }
214
215 public Collection<Option> optionsByType(final Set<Type> types) {
216 if (types == null)
217 throw new NullPointerException();
218
219 Collection<Option> allOptions = getAllOptions();
220 Collection<Option> filteredOptions =
221 new ArrayList<Option>(allOptions.size());
222 for (Option option : allOptions)
223 if (types.contains(option.getOptionType()))
224 filteredOptions.add(option);
225 // return Collections.unmodifiableCollection(filteredOptions);
226 return filteredOptions;
227 }
228
229 public Collection<Option> optionsByVisibility(
230 final Set<Visibility> visibilities)
231 {
232 if (visibilities == null)
233 throw new NullPointerException();
234
235 Collection<Option> allOptions = getAllOptions();
236 Collection<Option> filteredOptions =
237 new ArrayList<Option>(allOptions.size());
238 for (Option option : allOptions)
239 if (visibilities.contains(option.getOptionVisibility()))
240 filteredOptions.add(option);
241 // return Collections.unmodifiableCollection(filteredOptions);
242 return filteredOptions;
243 }
244
245 private void normalizeUserDirectory() {
246 Platform platform = StartupManager.INSTANCE.getPlatform();
247 File dir = new File(platform.getUserDirectory());
248 File prefs = new File(platform.getUserPrefs());
249
250 if (!dir.exists())
251 dir.mkdir();
252
253 if (!prefs.exists()) {
254 try {
255 File defaultPrefs = new File(platform.getDefaultPrefs());
256 StartupManager.INSTANCE.copy(defaultPrefs, prefs);
257 } catch (IOException e) {
258 System.err.println("Non-fatal error copying user preference template: "+e.getMessage());
259 }
260 }
261 }
262
263 public void readStartup() {
264 String contents;
265 String line;
266
267 File script =
268 new File(StartupManager.INSTANCE.getPlatform().getUserPrefs());
269 System.err.println("reading "+script);
270 if (script.getPath().length() == 0)
271 return;
272
273 try {
274 BufferedReader br = new BufferedReader(new FileReader(script));
275 while ((line = br.readLine()) != null) {
276 if (line.startsWith("#"))
277 continue;
278
279 contents = line.replace("=\"", "=");
280 String[] chunks = contents.replace("SET ", "").split("=");
281 if (chunks.length == 2) {
282 Option option = getOption(chunks[0]);
283 if (option != null)
284 option.fromPrefsFormat(line);
285 }
286 }
287 br.close();
288 } catch (IOException e) {
289 System.err.println("Non-fatal error reading the user preferences: "+e.getMessage());
290 }
291 }
292
293 public void writeStartup() {
294 File script =
295 new File(StartupManager.INSTANCE.getPlatform().getUserPrefs());
296 if (script.getPath().length() == 0)
297 return;
298
299 // TODO(jon): use filters when you've made 'em less stupid
300 String newLine =
301 StartupManager.INSTANCE.getPlatform().getNewLine();
302 OptionPlatform currentPlatform = convertToOptionPlatform();
303 StringBuilder contents = new StringBuilder();
304 for (Object[] arrayOption : blahblah) {
305 Option option = getOption((String)arrayOption[0]);
306 OptionPlatform platform = option.getOptionPlatform();
307 if (platform == OptionPlatform.ALL || platform == currentPlatform)
308 contents.append(option.toPrefsFormat() + newLine);
309 }
310
311 try {
312 BufferedWriter out =
313 new BufferedWriter(new FileWriter(script));
314 out.write(contents.toString());
315 out.close();
316 } catch (IOException e) {
317 e.printStackTrace();
318 }
319 }
320 }