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