001package Torello.Java; 002 003import java.io.BufferedWriter; 004import java.io.OutputStream; 005import java.io.OutputStreamWriter; 006import java.io.InputStream; 007import java.io.IOException; 008 009import java.util.Arrays; 010import java.util.Objects; 011 012/** 013 * Allows a user to pipe data directly from Java-Memory, and into an instance of 014 * <CODE>java.lang.Process</CODE>. 015 */ 016public class OSJavaPipe implements Cloneable 017{ 018 // ******************************************************************************************** 019 // ******************************************************************************************** 020 // Two Private-Fields, Two Inpsection Methods 021 // ******************************************************************************************** 022 // ******************************************************************************************** 023 024 025 private Object textOrDataToPipe; 026 private byte dataType; 027 028 /** 029 * Retrieve the Data-Type that is contained by this instance' internal PIPE-Data contents. 030 * @return The Static-Flag Constant associated with the Data-Type that has been assigned to 031 * this instance. 032 */ 033 public byte getDataType() 034 { return this.dataType; } 035 036 /** 037 * Retrieve the actual data, as a {@code java.lang.Object} contained by this instance. 038 * @return This class internal Data-Field 039 */ 040 public Object getPipeData() 041 { return this.textOrDataToPipe; } 042 043 044 // ******************************************************************************************** 045 // ******************************************************************************************** 046 // Static-Flag Constants, representing the Data's Type 047 // ******************************************************************************************** 048 // ******************************************************************************************** 049 050 051 /** 052 * Constant used to indicate that this class data-contents currently contain a non-null 053 * {@code int[]}-Array. 054 */ 055 public static final byte INT_ARR = 1; 056 057 /** 058 * Constant used to indicate that this class data-contents currently contain a non-null 059 * {@code byte[]}-Array. 060 */ 061 public static final byte BYTE_ARR = 2; 062 063 /** 064 * Constant used to indicate that this class data-contents currently contain a non-null 065 * {@code char[]}-Array. 066 */ 067 public static final byte CHAR_ARR = 3; 068 069 /** 070 * Constant used to indicate that this class data-contents currently contain a non-null 071 * {@code String}. 072 */ 073 public static final byte STRING = 4; 074 075 /** 076 * Constant used to indicate that this class data-contents currently contain a non-null 077 * {@code InputStream}. 078 */ 079 public static final byte INPUT_STREAM = 5; 080 081 082 // ******************************************************************************************** 083 // ******************************************************************************************** 084 // Constructors 085 // ******************************************************************************************** 086 // ******************************************************************************************** 087 088 089 private OSJavaPipe() { } 090 091 /** 092 * Constructs an instance of this class, assigning the input {@code int[]}-Array Parameter to 093 * the internal Data-Field. 094 * 095 * @param intArrData Any Java {@code int[]} Integer-Array. 096 * @throws NullPointerException if {@code 'intArrData'} is null. 097 */ 098 public OSJavaPipe(final int[] intArrData) 099 { this.setData(intArrData); } 100 101 /** 102 * Constructs an instance of this class, assigning the input {@code byte[]}-Array Parameter to 103 * the internal Data-Field. 104 * 105 * @param byteArrData Any Java {@code byte[]} Byte-Array. 106 * @throws NullPointerException if {@code 'byteArrData'} is null. 107 */ 108 public OSJavaPipe(final byte[] byteArrData) 109 { this.setData(byteArrData); } 110 111 /** 112 * Constructs an instance of this class, assigning the input {@code char[]}-Array Parameter to 113 * the internal Data-Field. 114 * 115 * @param charArrData Any Java {@code char[]} Char-Array. 116 * @throws NullPointerException if {@code 'charArrData'} is null. 117 */ 118 public OSJavaPipe(final char[] charArrData) 119 { this.setData(charArrData); } 120 121 /** 122 * Constructs an instance of this class, assigning the input {@code String}-Parameter to the 123 * internal Data-Field. 124 * 125 * @param strData Any Java {@code String}. 126 * @throws NullPointerException if {@code 'strData'} is null. 127 */ 128 public OSJavaPipe(final String strData) 129 { this.setData(strData); } 130 131 /** 132 * Constructs an instance of this class, assigning the {@code InputStream} Parameter to the 133 * internal Data-Field. 134 * 135 * @param inputStream Any Java Input-Stream. 136 * @throws NullPointerException if {@code 'inputStream'} is null. 137 */ 138 public OSJavaPipe(final InputStream inputStream) 139 { this.setData(inputStream); } 140 141 142 // ******************************************************************************************** 143 // ******************************************************************************************** 144 // This Class Main User-API for assigning some data 145 // ******************************************************************************************** 146 // ******************************************************************************************** 147 148 149 /** 150 * Sets the data to be used for piping into an Operating-System Process. 151 * @param intArrData Any Java {@code int[]} Integer-Array. 152 * @throws NullPointerException if {@code 'intArrData'} is null. 153 */ 154 public synchronized void setData(final int[] intArrData) 155 { 156 Objects.requireNonNull(intArrData, "The int[]-Array passed may not be null"); 157 158 this.textOrDataToPipe = intArrData; 159 this.dataType = INT_ARR; 160 } 161 162 /** 163 * Sets the data to be used for piping into an Operating-System Process. 164 * @param byteArrData Any Java {@code byte[]} Byte-Array. 165 * @throws NullPointerException if {@code 'byteArrData'} is null. 166 */ 167 public synchronized void setData(final byte[] byteArrData) 168 { 169 Objects.requireNonNull(byteArrData, "The byte[]-Array passed may not be null"); 170 171 this.textOrDataToPipe = byteArrData; 172 this.dataType = BYTE_ARR; 173 } 174 175 /** 176 * Sets the data to be used for piping into an Operating-System Process. 177 * @param charArrData Any Java {@code char[]} Char-Array. 178 * @throws NullPointerException if {@code 'charArrData'} is null. 179 */ 180 public synchronized void setData(final char[] charArrData) 181 { 182 Objects.requireNonNull(charArrData, "The char[]-Array passed may not be null"); 183 184 this.textOrDataToPipe = charArrData; 185 this.dataType = CHAR_ARR; 186 } 187 188 /** 189 * Sets the data to be used for piping into an Operating-System Process. 190 * @param strData Any Java {@code String}. 191 * @throws NullPointerException if {@code 'strData'} is null. 192 */ 193 public synchronized void setData(final String strData) 194 { 195 Objects.requireNonNull(strData, "The String-Data passed may not be null"); 196 197 this.textOrDataToPipe = strData; 198 this.dataType = STRING; 199 } 200 201 /** 202 * Sets the data to be used for piping into an Operating-System Process. 203 * @param inputStream Any Java Input-Stream. 204 * @throws NullPointerException if {@code 'inputStream'} is null. 205 */ 206 public synchronized void setData(final InputStream inputStream) 207 { 208 Objects.requireNonNull(inputStream, "The Input-Stream passed may not be null"); 209 210 this.textOrDataToPipe = inputStream; 211 this.dataType = INPUT_STREAM; 212 } 213 214 215 // ******************************************************************************************** 216 // ******************************************************************************************** 217 // Package-Private Method for writing this data to an input stream. 218 // ******************************************************************************************** 219 // ******************************************************************************************** 220 221 222 // Class OSCommands uses this Package-Private method to write the contents of the data-field 223 // inside this class to the "OutputStream". The OutputStream instance that is provided as 224 // input to this method is the one that is retrieved by invoking the method: 225 // 226 // Process.getOutputStream(). 227 // 228 // This OutputStream pipes the text/data that it receives directly to the Operating-System's 229 // Process. NOTE - this method is, essentially, the whole entire purpose of this class. Also, 230 // this class isn't so complicated, it's just a lot of wordy-explanations that help understand 231 // how to use java.lang.Process / Process.Redirect / and all those Input-Output Streams. It 232 // hopefully makes it a lot easier to use and extend this stuff. It certainly does for me. 233 // 234 // Again, this is Package-Private, and only invoked in one place, inside the "OSCommands" 235 // method. 236 237 synchronized void writeToPipe(final OutputStream os) throws IOException 238 { 239 switch (this.dataType) 240 { 241 case INT_ARR : 242 for (int i : (int[]) textOrDataToPipe) os.write(i); 243 break; 244 245 case BYTE_ARR : 246 for (byte b : (byte[]) textOrDataToPipe) os.write(b); 247 break; 248 249 case CHAR_ARR : 250 for (char c : (char[]) textOrDataToPipe) os.write(c); 251 break; 252 253 case STRING : 254 BufferedWriter writer = new BufferedWriter(new OutputStreamWriter(os)); 255 writer.write((String) textOrDataToPipe); 256 writer.flush(); 257 writer.close(); // Important to close the stream to signal end of input 258 break; 259 260 case INPUT_STREAM : 261 ((InputStream) textOrDataToPipe).transferTo(os); 262 break; 263 264 default: throw new UnreachableError(); 265 } 266 267 os.flush(); 268 os.close(); 269 } 270 271 272 // ******************************************************************************************** 273 // ******************************************************************************************** 274 // java.lang.Object 275 // ******************************************************************************************** 276 // ******************************************************************************************** 277 278 279 private static final String NAME = "OSJavaPipe Contents: "; 280 281 /** 282 * Generates a {@code String} this class. The returned {@code String} merely converts the 283 * contained data to a {@code String}, using Standard Java Methods. 284 * 285 * @return A simple representation of this class, as a {@code java.lang.String} 286 */ 287 public synchronized String toString() 288 { 289 switch (this.dataType) 290 { 291 case INT_ARR : return NAME + "int[]:\n" + StrPrint.abbrev( 292 Arrays.toString((int[]) this.textOrDataToPipe), 293 60, true, null, 120 294 ); 295 296 case BYTE_ARR : return NAME + "byte[]:\n" + StrPrint.abbrev( 297 Arrays.toString((byte[]) this.textOrDataToPipe), 298 60, true, null, 120 299 ); 300 301 case CHAR_ARR : return NAME + "char[]:\n" + StrPrint.abbrev( 302 Arrays.toString((char[]) this.textOrDataToPipe), 303 60, true, null, 120 304 ); 305 306 case STRING : return NAME + "String:\n" + StrPrint.abbrev 307 ((String) this.textOrDataToPipe, 60, true, null, 120); 308 309 case INPUT_STREAM : return NAME + "InputStream: Non-Null Data-Input\n"; 310 311 default: throw new UnreachableError(); 312 } 313 } 314 315 /** 316 * Checks for equality based on whether two instances have identical references. 317 * 318 * @param other Any Java Object. Only a valid sub-class of {@code OSJavaPipe} could possibly 319 * produce a {@code TRUE} return value 320 * 321 * @return {@code TRUE} if and only if this class' internal fields are <I>identical 322 * references in both {@code 'this'} and {@code 'other'}</I>. 323 */ 324 public synchronized boolean equals(Object other) 325 { 326 if (other == null) return false; 327 if (! (other instanceof OSJavaPipe)) return false; 328 329 final OSJavaPipe o = (OSJavaPipe) other; 330 331 return 332 Objects.equals(this.textOrDataToPipe, o.textOrDataToPipe) 333 && (this.dataType == o.dataType); 334 } 335 336 /** 337 * Makes a copy of {@code 'this'} instance and returns it. 338 * @return a clone of {@code 'this'} instance 339 */ 340 public synchronized Object clone() 341 { 342 // Private-Internal Zero-Argument Constructor 343 OSJavaPipe ret = new OSJavaPipe(); 344 345 ret.dataType = this.dataType; 346 ret.textOrDataToPipe = this.textOrDataToPipe; 347 348 return ret; 349 } 350 351 /** 352 * Produces a Hash-Code that may be used to place this instance-reference into a Hash-Table, 353 * Set or Map. The code produced is generated using whatever hash-code value is returned 354 * by this class' internal {@link #textOrDataToPipe} hash-code method. 355 * 356 * @return a hashcode 357 */ 358 public int hashCode() 359 { return this.textOrDataToPipe.hashCode(); } 360}