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.options; 029 030import java.awt.event.ItemEvent; 031import java.awt.event.ItemListener; 032 033import javax.swing.JComboBox; 034import javax.swing.SwingUtilities; 035 036import org.slf4j.LoggerFactory; 037 038import ch.qos.logback.classic.Level; 039import ch.qos.logback.classic.Logger; 040 041import edu.wisc.ssec.mcidasv.startupmanager.options.OptionMaster.OptionPlatform; 042import edu.wisc.ssec.mcidasv.startupmanager.options.OptionMaster.Type; 043import edu.wisc.ssec.mcidasv.startupmanager.options.OptionMaster.Visibility; 044import edu.wisc.ssec.mcidasv.util.McVGuiUtils; 045 046/** 047 * Representation of a choice allowing the user to select the global McIDAS-V 048 * logging level. 049 */ 050public class LoggerLevelOption extends AbstractOption { 051 052 /** 053 * {@code String} representation of Logback's {@literal "TRACE"} logging 054 * level. 055 */ 056 private static final String TRACE = Level.TRACE.toString(); 057 058 /** 059 * {@code String} representation of Logback's {@literal "DEBUG"} logging 060 * level. 061 */ 062 private static final String DEBUG = Level.DEBUG.toString(); 063 064 /** 065 * {@code String} representation of Logback's {@literal "INFO"} logging 066 * level. 067 */ 068 private static final String INFO = Level.INFO.toString(); 069 070 /** 071 * {@code String} representation of Logback's {@literal "WARN"} logging 072 * level. 073 */ 074 private static final String WARN = Level.WARN.toString(); 075 076 /** 077 * {@code String} representation of Logback's {@literal "ERROR"} logging 078 * level. 079 */ 080 private static final String ERROR = Level.ERROR.toString(); 081 082 /** 083 * {@code String} representation of Logback's {@literal "OFF"} logging 084 * level. 085 */ 086 private static final String OFF = Level.OFF.toString(); 087 088 /** 089 * {@code JComboBox} that will eventually contain logging levels to 090 * select. May be {@code null}. 091 */ 092 private JComboBox<String> comboBox; 093 094 /** 095 * {@code String} representation of the user's selection, or the default 096 * value provided to the constructor. 097 */ 098 private String currentChoice; 099 100 /** 101 * Create a startup option that allows the user to manipulate the global 102 * McIDAS-V logging level. <B>NOTE:</b> {@code null} is not a permitted 103 * value for any of this constructor's parameters. 104 * 105 * @param id Identifier for this startup option. 106 * @param label Brief description suitable for a GUI label. 107 * @param defaultValue Default value for this startup option. 108 * @param optionPlatform Platforms where this option may be applied. 109 * @param optionVisibility Whether or not the option is presented via the GUI. 110 * 111 * @throws IllegalArgumentException if {@code defaultValue} failed {@link #isValidValue(String)}. 112 */ 113 public LoggerLevelOption(String id, String label, String defaultValue, 114 OptionPlatform optionPlatform, Visibility optionVisibility) { 115 super(id, label, Type.LOGLEVEL, optionPlatform, optionVisibility); 116 if (!isValidValue(defaultValue)) { 117 throw new IllegalArgumentException("Default value '"+defaultValue+"' is not one of: TRACE, DEBUG, INFO, WARN, ERROR, or OFF."); 118 } 119 currentChoice = defaultValue; 120 } 121 122 /** 123 * Builds a {@link JComboBox} containing the logging levels to select. 124 * Defaults to the {@code String} specified in the constructor. 125 * 126 * @return {@code JComboBox} to present to the user. 127 */ 128 @Override public JComboBox<String> getComponent() { 129 comboBox = new JComboBox<>(new String[] { TRACE, DEBUG, INFO, WARN, ERROR, OFF }); 130 comboBox.setSelectedItem(currentChoice); 131 comboBox.addItemListener(new ItemListener() { 132 public void itemStateChanged(ItemEvent e) { 133 setValue(comboBox.getSelectedItem().toString()); 134 } 135 }); 136 137 McVGuiUtils.setComponentWidth(comboBox, McVGuiUtils.Width.ONEHALF); 138 return comboBox; 139 } 140 141 /** 142 * Returns the user's current selection (or the default value). 143 * 144 * @return Current selection or default value. 145 */ 146 @Override public String getValue() { 147 return currentChoice; 148 } 149 150 /** 151 * Stores the user's selected logging level. Note that this can be called 152 * from third-party or the GUI! If the call originates from the GUI, an 153 * infinite loop is avoided by using the {@link JComboBox#setSelectedItem(Object)} 154 * behavior that does <b>not</b> generate {@link ItemEvent ItemEvents} if 155 * the selection did not actually change. 156 * 157 * @param value {@code String} representation of the desired logging 158 * level. Should not be {@code null}. 159 * 160 * @throws IllegalArgumentException if {@code value} failed 161 * {@link #isValidValue(String)}. 162 */ 163 @Override public void setValue(String value) { 164 if (!isValidValue(value)) { 165 throw new IllegalArgumentException("Value '"+value+"' is not one of: TRACE, DEBUG, INFO, WARN, ERROR, or OFF."); 166 } 167 currentChoice = value; 168 Logger rootLogger = (Logger)LoggerFactory.getLogger(org.slf4j.Logger.ROOT_LOGGER_NAME); 169 rootLogger.setLevel(stringToLogback(value)); 170 SwingUtilities.invokeLater(new Runnable() { 171 @Override public void run() { 172 if (comboBox != null) { 173 comboBox.setSelectedItem(currentChoice); 174 } 175 } 176 }); 177 } 178 179 /** 180 * Converts a {@code String} value to the corresponding logging level. 181 * 182 * <p>This functionality is similar to 183 * {@link Level#toLevel(String, Level)}, but for this use case it is 184 * preferable to know if an invalid {@code value} was provided.</p> 185 * 186 * @param value Value to convert. 187 * 188 * @return Logging level. 189 * 190 * @throws IllegalArgumentException if {@code value} did not have a 191 * corresponding logging level. 192 */ 193 private static Level stringToLogback(final String value) { 194 Level level; 195 if (TRACE.equalsIgnoreCase(value)) { 196 level = Level.TRACE; 197 } else if (DEBUG.equalsIgnoreCase(value)) { 198 level = Level.DEBUG; 199 } else if (INFO.equalsIgnoreCase(value)) { 200 level = Level.INFO; 201 } else if (WARN.equalsIgnoreCase(value)) { 202 level = Level.WARN; 203 } else if (ERROR.equalsIgnoreCase(value)) { 204 level = Level.ERROR; 205 } else if (OFF.equalsIgnoreCase(value)) { 206 level = Level.OFF; 207 } else { 208 throw new IllegalArgumentException(); 209 } 210 return level; 211 } 212 213 /** 214 * Tests a {@code String} value to see if it has a corresponding logging 215 * level. 216 * 217 * @param value Value to test. 218 * 219 * @return {@code true} if-and-only-if passes a 220 * {@link String#equalsIgnoreCase(String)} check against {@link #TRACE}, 221 * {@link #DEBUG}, {@link #INFO}, {@link #WARN}, {@link #ERROR}, or 222 * {@link #OFF}. 223 */ 224 private static boolean isValidValue(final String value) { 225 return (TRACE.equalsIgnoreCase(value) || 226 DEBUG.equalsIgnoreCase(value) || 227 INFO.equalsIgnoreCase(value) || 228 WARN.equalsIgnoreCase(value) || 229 ERROR.equalsIgnoreCase(value) || 230 OFF.equalsIgnoreCase(value)); 231 } 232 233 /** 234 * {@code String} representation of the user's logging level selection. 235 * 236 * @return {@code String} that looks something like 237 * {@literal "[LoggerLevel@7825114a: currentChoice=INFO]"}. 238 */ 239 public String toString() { 240 return String.format("[LoggerLevelOption@%x: currentChoice=%s]", 241 hashCode(), currentChoice); 242 } 243}