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 t = (Terminal) Thread.currentThread().getContextClassLoader().loadClass(type).newInstance(); 86 } 87 catch (Exception e) { 88 throw new IllegalArgumentException(MessageFormat.format("Invalid terminal type: {0}", type), e); 89 } 90 } 91 } 92 } 93 catch (Exception e) { 94 Log.error("Failed to construct terminal; falling back to unsupported", e); 95 t = new UnsupportedTerminal(); 96 } 97 98 Log.debug("Created Terminal: ", t); 99 100 try { 101 t.init(); 102 } 103 catch (Throwable e) { 104 Log.error("Terminal initialization failed; falling back to unsupported", e); 105 return new UnsupportedTerminal(); 106 } 107 108 return t; 109 } 110 111 public static synchronized void reset() { 112 term = null; 113 } 114 115 public static synchronized void resetIf(final Terminal t) { 116 if(t == term) { 117 reset(); 118 } 119 } 120 121 public static enum Type 122 { 123 AUTO, 124 WINDOWS, 125 UNIX, 126 NONE 127 } 128 129 public static synchronized void configure(final String type) { 130 checkNotNull(type); 131 System.setProperty(JLINE_TERMINAL, type); 132 } 133 134 public static synchronized void configure(final Type type) { 135 checkNotNull(type); 136 configure(type.name().toLowerCase()); 137 } 138 139 // 140 // Flavor Support 141 // 142 143 public static enum Flavor 144 { 145 WINDOWS, 146 UNIX 147 } 148 149 private static final Map<Flavor, Callable<? extends Terminal>> FLAVORS = new HashMap<>(); 150 151 static { 152 // registerFlavor(Flavor.WINDOWS, AnsiWindowsTerminal.class); 153 // registerFlavor(Flavor.UNIX, UnixTerminal.class); 154 registerFlavor(Flavor.WINDOWS, WindowsTerminal :: new); 155 registerFlavor(Flavor.UNIX, UnixTerminal :: new); 156 } 157 158 public static synchronized Terminal get() { 159 if (term == null) { 160 term = create(); 161 } 162 return term; 163 } 164 165 public static Terminal getFlavor(final Flavor flavor) throws Exception { 166 return FLAVORS.getOrDefault(flavor, () -> {throw new InternalError();}).call(); 167 } 168 169 public static void registerFlavor(final Flavor flavor, final Callable<? extends Terminal> sup) { 170 FLAVORS.put(flavor, sup); 171 } 172 173 }