001/* 002 * To change this template, choose Tools | Templates 003 * and open the template in the editor. 004 */ 005package org.anarres.qemu.exec; 006 007import java.io.IOException; 008import java.util.ArrayList; 009import java.util.Arrays; 010import java.util.List; 011import javax.annotation.CheckForNull; 012import javax.annotation.Nonnull; 013import org.anarres.qemu.exec.recipe.QEmuRecipe; 014import org.anarres.qemu.exec.util.QEmuCommandLineUtils; 015import org.anarres.qemu.exec.util.QEmuIdAllocator; 016import org.anarres.qemu.exec.util.QEmuOptionsList; 017 018/** 019 * An execution command line for a QEmu process. 020 * 021 * A QEmuCommandLine is a tree of {@link QEmuOption}, not a list 022 * (see {@link QEmuOptionsList}). 023 * 024 * Disk and bus IDs within a QEmuOption may be allocated using the embedded 025 * {@link QEmuIdAllocator} in order to guarantee uniqueness. 026 * 027 * For convenience, composites of commonly used options are provided as 028 * {@link QEmuRecipe}s. 029 * 030 * @author shevek 031 */ 032public class QEmuCommandLine { 033 034 private final QEmuIdAllocator allocator; 035 @Nonnull 036 private QEmuArchitecture architecture; 037 @CheckForNull 038 private String commandName; 039 private final List<QEmuOption> options = new ArrayList<QEmuOption>(); 040 041 private QEmuCommandLine(@Nonnull QEmuArchitecture architecture, @Nonnull QEmuIdAllocator allocator) { 042 this.allocator = allocator; 043 this.architecture = architecture; 044 } 045 046 public QEmuCommandLine(@Nonnull QEmuArchitecture architecture) { 047 this(architecture, new QEmuIdAllocator()); 048 } 049 050 public QEmuCommandLine(@Nonnull QEmuCommandLine prototype) { 051 this(prototype.getArchitecture(), new QEmuIdAllocator(prototype.getAllocator())); 052 addOptions(prototype.getOptions()); 053 } 054 055 @Nonnull 056 public QEmuArchitecture getArchitecture() { 057 return architecture; 058 } 059 060 public void setArchitecture(@Nonnull QEmuArchitecture architecture) { 061 this.architecture = architecture; 062 } 063 064 @Nonnull 065 public QEmuIdAllocator getAllocator() { 066 return allocator; 067 } 068 069 /** Returns all the options in this command line. */ 070 @Nonnull 071 public List<? extends QEmuOption> getOptions() { 072 return options; 073 } 074 075 /** 076 * Returns the name (possibly with path) of the binary used to execute qemu. 077 * 078 * This is derived from {@link #getArchitecture()} if not explicitly 079 * set using {@link #setCommandName(java.lang.String)}. 080 */ 081 @Nonnull 082 public String getCommandName() { 083 String c = commandName; 084 if (c != null) 085 return c; 086 return getArchitecture().getCommand(); 087 } 088 089 public void setCommandName(@Nonnull String commandName) { 090 this.commandName = commandName; 091 } 092 093 private static <T extends QEmuOption> void getOptions(@Nonnull List<? super T> out, @Nonnull Iterable<? extends QEmuOption> in, @Nonnull Class<T> type) { 094 for (QEmuOption option : in) { 095 if (type.isInstance(option)) 096 out.add(type.cast(option)); 097 if (option instanceof QEmuOptionsList) 098 getOptions(out, (QEmuOptionsList) option, type); 099 } 100 } 101 102 /** 103 * Returns all options of the specified type in this command line. 104 */ 105 @Nonnull 106 public <T extends QEmuOption> List<? extends T> getOptions(@Nonnull Class<T> type) { 107 List<T> out = new ArrayList<T>(); 108 getOptions(out, getOptions(), type); 109 return out; 110 } 111 112 /** 113 * Returns the first option of the specified type in this command line. 114 * 115 * If there are no options of type <code>type</code>, null is returned. 116 * If there is more than one option of type <code>type</code>, the subsequent 117 * options are ignored. 118 */ 119 @CheckForNull 120 public <T extends QEmuOption> T getOption(@Nonnull Class<T> type) { 121 List<? extends T> options = getOptions(type); 122 if (options.isEmpty()) 123 return null; 124 return options.iterator().next(); 125 } 126 127 public void addOptions(@Nonnull Iterable<? extends QEmuOption> options) { 128 for (QEmuOption option : options) 129 if (option != null) 130 this.options.add(option); 131 } 132 133 public void addOptions(@Nonnull QEmuOption... options) { 134 addOptions(Arrays.asList(options)); 135 } 136 137 @Nonnull 138 public List<String> toCommandWords() { 139 List<String> line = new ArrayList<String>(); 140 line.add(getCommandName()); 141 for (QEmuOption option : options) 142 option.appendTo(line); 143 return line; 144 } 145 146 /** 147 * Executes this QEmuCommandLine and returns a new {@link Process}. 148 */ 149 @Nonnull 150 public Process exec() throws IOException { 151 List<String> commandWords = toCommandWords(); 152 ProcessBuilder builder = new ProcessBuilder(commandWords); 153 // TODO: Use Redirect to send the I/O somewhere useful. 154 QEmuCommandLineUtils.redirectIO(builder); 155 return builder.start(); 156 } 157 158 @Override 159 public String toString() { 160 StringBuilder buf = new StringBuilder(); 161 for (String word : toCommandWords()) { 162 if (buf.length() > 0) 163 buf.append(' '); 164 buf.append(word); 165 } 166 return buf.toString(); 167 } 168}