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 jline;
  10 
  11 import java.text.MessageFormat;
  12 import java.util.HashMap;
  13 import java.util.Map;
  14 
  15 import jline.internal.Configuration;
  16 import jline.internal.Log;
  17 import jline.internal.Preconditions;
  18 
  19 import static 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, Class<? extends Terminal>> FLAVORS = new HashMap<Flavor, Class<? extends Terminal>>();
 150 
 151     static {
 152         registerFlavor(Flavor.WINDOWS, AnsiWindowsTerminal.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 }