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 }