1 /* |
|
2 * Copyright (c) 2002-2016, 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 |
|
15 import jdk.internal.jline.internal.Configuration; |
|
16 import jdk.internal.jline.internal.Log; |
|
17 import static jdk.internal.jline.internal.Preconditions.checkNotNull; |
|
18 |
|
19 /** |
|
20 * Creates terminal instances. |
|
21 * |
|
22 * @author <a href="mailto:jason@planet57.com">Jason Dillon</a> |
|
23 * @since 2.0 |
|
24 */ |
|
25 public class TerminalFactory |
|
26 { |
|
27 public static final String JLINE_TERMINAL = "jline.terminal"; |
|
28 |
|
29 public static final String AUTO = "auto"; |
|
30 |
|
31 public static final String UNIX = "unix"; |
|
32 |
|
33 public static final String OSV = "osv"; |
|
34 |
|
35 public static final String WIN = "win"; |
|
36 |
|
37 public static final String WINDOWS = "windows"; |
|
38 |
|
39 public static final String FREEBSD = "freebsd"; |
|
40 |
|
41 public static final String NONE = "none"; |
|
42 |
|
43 public static final String OFF = "off"; |
|
44 |
|
45 public static final String FALSE = "false"; |
|
46 |
|
47 private static Terminal term = null; |
|
48 |
|
49 public static synchronized Terminal create() { |
|
50 return create(null); |
|
51 } |
|
52 |
|
53 public static synchronized Terminal create(String ttyDevice) { |
|
54 if (Log.TRACE) { |
|
55 //noinspection ThrowableInstanceNeverThrown |
|
56 Log.trace(new Throwable("CREATE MARKER")); |
|
57 } |
|
58 |
|
59 String defaultType = "dumb".equals(System.getenv("TERM")) ? NONE : AUTO; |
|
60 String type = Configuration.getString(JLINE_TERMINAL, defaultType); |
|
61 |
|
62 Log.debug("Creating terminal; type=", type); |
|
63 |
|
64 Terminal t; |
|
65 try { |
|
66 String tmp = type.toLowerCase(); |
|
67 |
|
68 if (tmp.equals(UNIX)) { |
|
69 t = getFlavor(Flavor.UNIX); |
|
70 } |
|
71 else if (tmp.equals(OSV)) { |
|
72 t = getFlavor(Flavor.OSV); |
|
73 } |
|
74 else if (tmp.equals(WIN) || tmp.equals(WINDOWS)) { |
|
75 t = getFlavor(Flavor.WINDOWS); |
|
76 } |
|
77 else if (tmp.equals(NONE) || tmp.equals(OFF) || tmp.equals(FALSE)) { |
|
78 if (System.getenv("INSIDE_EMACS") != null) { |
|
79 // emacs requires ansi on and echo off |
|
80 t = new UnsupportedTerminal(true, false); |
|
81 } else { |
|
82 // others the other way round |
|
83 t = new UnsupportedTerminal(false, true); |
|
84 } |
|
85 } |
|
86 else { |
|
87 if (tmp.equals(AUTO)) { |
|
88 String os = Configuration.getOsName(); |
|
89 Flavor flavor = Flavor.UNIX; |
|
90 if (os.contains(WINDOWS)) { |
|
91 flavor = Flavor.WINDOWS; |
|
92 } else if (System.getenv("OSV_CPUS") != null) { |
|
93 flavor = Flavor.OSV; |
|
94 } |
|
95 t = getFlavor(flavor, ttyDevice); |
|
96 } |
|
97 else { |
|
98 try { |
|
99 @SuppressWarnings("deprecation") |
|
100 Object o = Thread.currentThread().getContextClassLoader().loadClass(type).newInstance(); |
|
101 t = (Terminal) o; |
|
102 } |
|
103 catch (Exception e) { |
|
104 throw new IllegalArgumentException(MessageFormat.format("Invalid terminal type: {0}", type), e); |
|
105 } |
|
106 } |
|
107 } |
|
108 } |
|
109 catch (Exception e) { |
|
110 Log.error("Failed to construct terminal; falling back to unsupported", e); |
|
111 t = new UnsupportedTerminal(); |
|
112 } |
|
113 |
|
114 Log.debug("Created Terminal: ", t); |
|
115 |
|
116 try { |
|
117 t.init(); |
|
118 } |
|
119 catch (Throwable e) { |
|
120 Log.error("Terminal initialization failed; falling back to unsupported", e); |
|
121 return new UnsupportedTerminal(); |
|
122 } |
|
123 |
|
124 return t; |
|
125 } |
|
126 |
|
127 public static synchronized void reset() { |
|
128 term = null; |
|
129 } |
|
130 |
|
131 public static synchronized void resetIf(final Terminal t) { |
|
132 if(t == term) { |
|
133 reset(); |
|
134 } |
|
135 } |
|
136 |
|
137 public static enum Type |
|
138 { |
|
139 AUTO, |
|
140 WINDOWS, |
|
141 UNIX, |
|
142 OSV, |
|
143 NONE |
|
144 } |
|
145 |
|
146 public static synchronized void configure(final String type) { |
|
147 checkNotNull(type); |
|
148 System.setProperty(JLINE_TERMINAL, type); |
|
149 } |
|
150 |
|
151 public static synchronized void configure(final Type type) { |
|
152 checkNotNull(type); |
|
153 configure(type.name().toLowerCase()); |
|
154 } |
|
155 |
|
156 // |
|
157 // Flavor Support |
|
158 // |
|
159 |
|
160 public static enum Flavor |
|
161 { |
|
162 WINDOWS, |
|
163 UNIX, |
|
164 OSV |
|
165 } |
|
166 |
|
167 private static final Map<Flavor, TerminalConstructor> FLAVORS = new HashMap<>(); |
|
168 |
|
169 static { |
|
170 registerFlavor(Flavor.WINDOWS, ttyDevice -> new WindowsTerminal()); |
|
171 registerFlavor(Flavor.UNIX, ttyDevice -> new UnixTerminal(ttyDevice)); |
|
172 registerFlavor(Flavor.OSV, ttyDevice -> new OSvTerminal()); |
|
173 } |
|
174 |
|
175 public static synchronized Terminal get(String ttyDevice) { |
|
176 // The code is assuming we've got only one terminal per process. |
|
177 // Continuing this assumption, if this terminal is already initialized, |
|
178 // we don't check if it's using the same tty line either. Both assumptions |
|
179 // are a bit crude. TODO: check single terminal assumption. |
|
180 if (term == null) { |
|
181 term = create(ttyDevice); |
|
182 } |
|
183 return term; |
|
184 } |
|
185 |
|
186 public static synchronized Terminal get() { |
|
187 return get(null); |
|
188 } |
|
189 |
|
190 public static Terminal getFlavor(final Flavor flavor) throws Exception { |
|
191 return getFlavor(flavor, null); |
|
192 } |
|
193 |
|
194 @SuppressWarnings("deprecation") |
|
195 public static Terminal getFlavor(final Flavor flavor, String ttyDevice) throws Exception { |
|
196 TerminalConstructor factory = FLAVORS.get(flavor); |
|
197 if (factory != null) { |
|
198 return factory.createTerminal(ttyDevice); |
|
199 } else { |
|
200 throw new InternalError(); |
|
201 } |
|
202 } |
|
203 |
|
204 public static void registerFlavor(final Flavor flavor, final TerminalConstructor factory) { |
|
205 FLAVORS.put(flavor, factory); |
|
206 } |
|
207 |
|
208 public interface TerminalConstructor { |
|
209 public Terminal createTerminal(String str) throws Exception; |
|
210 } |
|
211 } |
|