1 /* 2 * Copyright (c) 2002-2012, the original author or authors. 3 * 4 * This software is distributable under the BSD license. See the terms of the 5 * BSD license in the documentation provided with this software. 6 * 7 * http://www.opensource.org/licenses/bsd-license.php 8 */ 9 package jdk.internal.jline; 10 11 import java.text.MessageFormat; 12 import java.util.HashMap; 13 import java.util.Map; 14 import java.util.concurrent.Callable; 15 16 import jdk.internal.jline.internal.Configuration; 17 import jdk.internal.jline.internal.Log; 18 import jdk.internal.jline.internal.Preconditions; 19 import static jdk.internal.jline.internal.Preconditions.checkNotNull; 20 21 /** 22 * Creates terminal instances. 23 * 24 * @author <a href="mailto:jason@planet57.com">Jason Dillon</a> 25 * @since 2.0 26 */ 27 public class TerminalFactory 28 { 29 public static final String JLINE_TERMINAL = "jline.terminal"; 30 31 public static final String AUTO = "auto"; 32 33 public static final String UNIX = "unix"; 34 35 public static final String WIN = "win"; 36 37 public static final String WINDOWS = "windows"; 38 39 public static final String NONE = "none"; 40 41 public static final String OFF = "off"; 42 43 public static final String FALSE = "false"; 44 45 private static Terminal term = null; 46 47 public static synchronized Terminal create() { 48 if (Log.TRACE) { 49 //noinspection ThrowableInstanceNeverThrown 50 Log.trace(new Throwable("CREATE MARKER")); 51 } 52 53 String type = Configuration.getString(JLINE_TERMINAL, AUTO); 54 if ("dumb".equals(System.getenv("TERM"))) { 55 type = "none"; 56 Log.debug("$TERM=dumb; setting type=", type); 57 } 58 59 Log.debug("Creating terminal; type=", type); 60 61 Terminal t; 62 try { 63 String tmp = type.toLowerCase(); 64 65 if (tmp.equals(UNIX)) { 66 t = getFlavor(Flavor.UNIX); 67 } 68 else if (tmp.equals(WIN) | tmp.equals(WINDOWS)) { 69 t = getFlavor(Flavor.WINDOWS); 70 } 71 else if (tmp.equals(NONE) || tmp.equals(OFF) || tmp.equals(FALSE)) { 72 t = new UnsupportedTerminal(); 73 } 74 else { 75 if (tmp.equals(AUTO)) { 76 String os = Configuration.getOsName(); 77 Flavor flavor = Flavor.UNIX; 78 if (os.contains(WINDOWS)) { 79 flavor = Flavor.WINDOWS; 80 } 81 t = getFlavor(flavor); 82 } 83 else { 84 try { 85 @SuppressWarnings("deprecation") 86 Object o = Thread.currentThread().getContextClassLoader().loadClass(type).newInstance(); 87 t = (Terminal) o; 88 } 89 catch (Exception e) { 90 throw new IllegalArgumentException(MessageFormat.format("Invalid terminal type: {0}", type), e); 91 } 92 } 93 } 94 } 95 catch (Exception e) { 96 Log.error("Failed to construct terminal; falling back to unsupported", e); 97 t = new UnsupportedTerminal(); 98 } 99 100 Log.debug("Created Terminal: ", t); 101 102 try { 103 t.init(); 104 } 105 catch (Throwable e) { 106 Log.error("Terminal initialization failed; falling back to unsupported", e); 107 return new UnsupportedTerminal(); 108 } 109 110 return t; 111 } 112 113 public static synchronized void reset() { 114 term = null; 115 } 116 117 public static synchronized void resetIf(final Terminal t) { 118 if(t == term) { 119 reset(); 120 } 121 } 122 123 public static enum Type 124 { 125 AUTO, 126 WINDOWS, 127 UNIX, 128 NONE 129 } 130 131 public static synchronized void configure(final String type) { 132 checkNotNull(type); 133 System.setProperty(JLINE_TERMINAL, type); 134 } 135 136 public static synchronized void configure(final Type type) { 137 checkNotNull(type); 138 configure(type.name().toLowerCase()); 139 } 140 141 // 142 // Flavor Support 143 // 144 145 public static enum Flavor 146 { 147 WINDOWS, 148 UNIX 149 } 150 151 private static final Map<Flavor, Callable<? extends Terminal>> FLAVORS = new HashMap<>(); 152 153 static { 154 // registerFlavor(Flavor.WINDOWS, AnsiWindowsTerminal.class); 155 // registerFlavor(Flavor.UNIX, UnixTerminal.class); 156 registerFlavor(Flavor.WINDOWS, WindowsTerminal :: new); 157 registerFlavor(Flavor.UNIX, UnixTerminal :: new); 158 } 159 160 public static synchronized Terminal get() { 161 if (term == null) { 162 term = create(); 163 } 164 return term; 165 } 166 167 public static Terminal getFlavor(final Flavor flavor) throws Exception { 168 return FLAVORS.getOrDefault(flavor, () -> {throw new InternalError();}).call(); 169 } 170 171 public static void registerFlavor(final Flavor flavor, final Callable<? extends Terminal> sup) { 172 FLAVORS.put(flavor, sup); 173 } 174 175 }