package com.perforce.client.api; import java.io.*; import java.util.*; /** * I/O for Perforce commands. * * The ClientUser class is used for all client-side input and output. * This class implements methods that return output from the server to * the user after a command is invoked, and gather input from the user * when needed. * * <p> Member functions in this class are used to format and display * server output, invoke external programs (such as text editors, diff * tools, and merge tools), gather input for processing by the server, * and to handle errors. * * <p> Customized functionality in a Perforce client application is most * typically implemented by subclassing ClientUser. In order to enable such * customization, nearly all of ClientUser's methods are virtual. The default * implementations are used in the p4 command-line client. * */ public class ClientUser { long instance; private boolean amFinished = false; private boolean releaseAfterFinished = true; /** * Makes a ClientUser object. By default this object cleans up the native * reference when the finished() method is called. * **/ public ClientUser() { this(true); } /** * Makes a ClientUser object. * * @param releaseAfterFinished If you want the ClientUser to * clean up the native global reference * when the finished() method is called. * **/ public ClientUser(boolean releaseAfterFinished) { this.instance = nNewInstance(); setReleaseAfterFinished(releaseAfterFinished); } protected void finalize() throws Throwable { nDeleteInstance(this.instance); this.instance = 0; super.finalize(); } /** * Creates a FileSys object for reading and writing files in the client workspace. * * This method is a wrapper for FileSys::Create(). * * <p> ClientUser::File() is generally called whenever it's necessary * to manipulate files in the client workspace. For example, a p4 sync, * p4 edit, or p4 revert makes one call to File() for each workspace * file with which the command interacts. * * <p> An alternate implementation might return a subclass of FileSys. * For example, if you have defined a class MyFileSys and want your * MyClientUser class to use members of this class rather than the base * FileSys, reimplement File() to return a MyFileSys instead: * * @param type FileSystemType * @return never * @exception UnsupportedOperationException always */ public FileSys file(long type) throws UnsupportedOperationException { throw new UnsupportedOperationException(); } /** * A request from the server asking for input from the user. * * Provides data from stdin to <tt>p4 <var>command</var> -i</tt>. * * <p> Any command that edits a specification can take the <tt>-i</tt> * option; this method supplies the data for the specification. In the * default implementation, the data comes from stdin, but an alternate * implementation can accept the data from any source. This method is * the only way to send a specification to the server without first * putting it into a local file. * * @return the text that the user input. * * **/ public String inputData() throws P4ClientException { InputStreamReader isr = new InputStreamReader(System.in); LineNumberReader lr = new LineNumberReader(isr); try { try { return lr.readLine(); } finally { lr.close(); isr.close(); } } catch (IOException e) { throw new P4ClientException(e); } } /** * Processes error data after a failed command. * * <p> The default implementation formats the error with {@link P4Error#fmt} * and outputs the result with {@link #outputError outputError()}. 2002.1 * and newer servers do not call <tt>handleError()</tt> to display errors. * Instead, they call {@link #message message()}. The default implementation * of {@link #message message()} calls <tt>handleError()</tt> if its * argument is a genuine error; as a result, older code that uses * <tt>handleError()</tt> can be used with the newer API and newer servers * so long as the default implementation of {@link #message message()} is * retained. * * @param err The top error from an Error object */ public void handleError(P4ClientException err) { // } /** * Displays a message as an error. * * The default implementation sends its argument to stderr. * This method is called by methods like {@link #handleError}. * * @param errBuf The error message */ public void outputError(String errBuf) { System.err.print(errBuf); } /** * Outputs tabular data. * * This method is called by the server during most Perforce commands; its * most common use is to display listings of information about files. Any * output not printed with <tt>outputInfo()</tt> is typically printed with * {@link #outputText outputText()}. Running <tt>p4 -s * <var>command</var></tt> indicates whether any given line of output is * "info" or "text". * * <p> In the default implementation of {@link #outputInfo outputInfo()}, * one <tt>"..."</tt> string is printed per "level". Values given as * "levels" are either 0, 1, or 2. The "data" passed is generally one * line, without a line break; <tt>outputInfo()</tt> adds the newline when * it prints the output. * * <p> To capture information directly from Perforce commands for parsing * or storing rather than output to stdout, it is usually necessary to use * an alternate implementation of <tt>outputInfo()</tt>. * * <p> 2002.1 and newer servers do not call <tt>outputInfo()</tt> to display * information. Instead, they call {@link #message message()}. The default * implementation of {@link #message message()} calls <tt>outputInfo()</tt> * if its argument represents information instead of an error; older code * that uses <tt>outputInfo()</tt> can be used with the newer API and newer * servers, so long as the default implementation of * {@link #message message()} is retained. * * @param level The indentation "level" of the output * @param data One line of output */ public void outputInfo(char level, String data) { System.err.println(data); } /** * Outputs binary data. * * <p> The default implementation of <tt>outputBinary()</tt> writes the * contents of a binary file to stdout. A call to <tt>outputBinary()</tt> is * typically the result of running <tt>p4 print</tt> on a binary file: * * <pre> * p4 print //depot/file.jpg > newfile.jpg * </pre> * * <p> WARNING: any file that is longer than <tt>Integer.MAX_INTEGER</tt> * will overrun the data buffer and crash the JVM. * * @param data The data to output * @deprecated needs to be replaced with stream-based output */ public void outputBinary(byte[] data) { // TODO: replace outputBinary with stream-based output try { System.out.write(data); } catch (IOException e) { e.printStackTrace(); } } /** * Outputs textual data. * * <p> The default implementation of <tt>outputText()</tt> writes the * contents of an ascii file to stdout. A call to <tt>outputText()</tt> is * typically the result of running <tt>p4 print</tt> on an ascii file: * * <pre> * p4 print -q file.txt * </pre> * * <p> WARNING: any file that is longer than <tt>Integer.MAX_INTEGER</tt> * will overrun the data buffer and crash the JVM. * * @param data The data to output * @deprecated needs to be replaced with stream-based output */ public void outputText(char[] data) { // TODO: replace outputText with stream-based output System.out.print(data); } /** * Processes tagged output. * * <p> Normally, the only Perforce command that sends output through * this method is <tt>p4 fstat</tt>, which always returns tagged * output. Some other commands can be made to return tagged output * by setting the "tag" protocol variable, in which case the output * is in the form of a {@link StrDict} suitable for passing to this * method for processing. * * <p> It is generally easier to deal with tagged output than it is to * parse standard output. The default implementation of this method * passes each variable/value pair in the {@link StrDict} to * {@link #outputInfo outputInfo()} as a line of text at level "1", * with the exception of the "func" var, which it skips. * Alternate implementations can use tagged output to extract the * pieces of information desired from a given command. * * <p> Other commands provide {@link StrDict}s with different * variable/value pairs that can be processed in similar ways; use * <tt>p4 -Ztag <var>command</var></tt> to get an understanding of * what sort of information to expect. * * @param sd A {@link StrDict} containing the information * returned by the command * @see ClientApi#setProtocol */ public void outputStat(StrDict sd) { StringBuffer msg = new StringBuffer(512); Iterator<Map.Entry<String, String>> enti = sd.entrySet().iterator(); // Dump out the variables, using the GetVar(x) interface. while (enti.hasNext()) { Map.Entry<String, String> ent = enti.next(); final String var = ent.getKey(); // Don't display the function (duh), which is only relevant to rpc. if (var.equals("func")) continue; msg.append(var); msg.append(" "); msg.append(ent.getValue()); // otherAction and otherOpen go at level 2, as per 99.1 + earlier char level = var.startsWith("other") ? '2' : '1'; outputInfo(level, msg.toString()); } // blank line outputInfo('0', ""); } /** * Prompts the user and gets a response. * * @param msg The message with which to prompt the user * @param noEcho Whether echo should be turned off at the console * @return the user's response */ public String prompt(final String msg, int noEcho) throws P4ClientException { System.out.println(msg); try { return new LineNumberReader(new InputStreamReader(System.in)).readLine(); } catch (IOException e) { throw new P4ClientException(e); } } /** * Outputs an error and prompts for a keystroke to continue. * * <p> The default implementation of {@link #errorPause errorPause()} * consists solely of calls to {@link #outputError outputError()} and * {@link #prompt prompt()}. Within a GUI, the warning text and "OK" * button are probably bundled into a single dialog, so overriding * {@link #errorPause errorPause()} is a better approach * than overriding {@link #outputError outputError()} and * {@link #prompt prompt()} separately. * * @param errBuf The error message to be printed * @throws P4ClientException */ public void errorPause(String errBuf) throws P4ClientException { outputError(errBuf); prompt("Hit enter/return to continue...", 0); } /** * Bring up the given file in a text editor. * Called by all p4 commands that edit specifications. * * <p> The FileSys argument refers to a client temp file that contains * the specification that is to be given to the server. This method does * not send the file to the server; its only job is to modify the file. * In the default implementation, it does not return until the editor has * returned. * * <p> There is also a three-argument version of the C++ method * <tt>Edit()</tt>, for which the default two-argument version is simply a * wrapper. The three-argument version takes an <tt>Enviro</tt> object as * an additional argument, and the two-argument version simply passes the * member variable <tt>enviro</tt> as this argument. Only the two-argument * version is virtual. * * @param f1 The file to be edited * @throws P4ClientException if perforce signals an error */ public void edit(FileSys f1) throws P4ClientException { P4Error err = new P4Error(); System.err.println("Edit"); nEdit(this.instance, f1.instance, err.instance); err.checkException(); } /** * This method is used by <tt>p4 diff</tt> and to display diffs from an * interactive <tt>p4 resolve</tt>. If no external diff program is * specified, the diff is carried out with a <tt>Diff</tt> object (part of * the Perforce client API); otherwise, this method simply calls the * specified external program. As with {@link #merge}, the external program * is invoked with <tt>ClientUser::RunCmd()</tt>. * * <p> If <var>doPage</var> is nonzero and the <tt>P4PAGER</tt> environment * variable is set, the output is piped through the executable specified by * <tt>P4PAGER</tt>. * * @param f1 The first file to be diffed * @param f2 The second file to be diffed * @param doPage Should output be paged? * @param diffFlags Flags to diff routine * @throws P4ClientException if perforce signals an error */ public void diff(FileSys f1, FileSys f2, boolean doPage, String diffFlags) throws P4ClientException { P4Error err = new P4Error(); nDiff(this.instance, f1.instance, f2.instance, doPage, diffFlags, err.instance); err.checkException(); } /** * This method is called if the "m" option is selected during an interactive * resolve. * * @param base A temp file built from the depot revision * that is the "base" of the resolve * @param leg1 A temp file built from the depot revision * that is the "theirs" of the resolve * @param leg2 The local workspace file * that is the "yours" of the resolve * @param result A temp file in which to construct * the new revision of "yours" * @throws P4ClientException if perforce signals an error */ public void merge(FileSys base, FileSys leg1, FileSys leg2, FileSys result) throws P4ClientException { P4Error err = new P4Error(); nMerge(this.instance, base.instance, leg1.instance, leg2.instance, result.instance, err.instance); err.checkException(); } /** * Displays a block of help text to the user. * * Used by p4 resolve but not p4 help. * * @param help An array of Strings containing the help text */ public void help(String[] help) { for (int i = 0; i < help.length; i++) { System.out.println(help[i]); } } /** * Not implemented. * * @param err TBD * @see #outputInfo */ void message(P4Error err) { // TODO: implement JNI hooks for message } public final void releaseNativeGlobalRef() { nReleaseNativeGlobalRef(this.instance); } public final void setReleaseAfterFinished(boolean releaseAfterFinished) { synchronized(this) { releaseAfterFinished = true; } // // Maybe the finished method was already called by the time we get here. // if (amFinished) { releaseNativeGlobalRef(); } } /** * Called after client commands finish. * * This function is called by the server at the end of every Perforce * command, but in its default implementation, it has no effect. The default * implementation of this function is empty - it takes nothing, does * nothing, and returns nothing. */ public void finished() { } // // CALLBACKS FROM NATIVE CODE // private final String inputDataCALLBACK(int errorInstance) { String result = null; P4Error catcher = P4Error.makeJavaPeer(errorInstance); try { result = inputData(); } catch (P4ClientException e) { catcher.convertException(e); } return result; } private final void handleErrorCALLBACK(int errorInstance) { P4Error peer = P4Error.makeJavaPeer(errorInstance); try { peer.checkException(); } catch (P4ClientException err) { handleError(err); } } private final void outputErrorCALLBACK(String errBuf) { outputError(errBuf); } private final void outputInfoCALLBACK(char level, String data) { outputInfo(level, data); } private final void outputBinaryCALLBACK(byte[] data) { outputBinary(data); } private final void outputTextCALLBACK(char[] data) { outputText(data); } private final void outputStatCALLBACK(int strDictInstance) { outputStat(new StrDict(strDictInstance){}); } private final String promptCALLBACK(final String msg, int noEcho, int errInstance) { String result = null; P4Error catcher = P4Error.makeJavaPeer(errInstance); try { result = prompt(msg, noEcho); } catch (P4ClientException e) { catcher.convertException(e); } return result; } private final void errorPauseCALLBACK(String errBuf, int errInstance) { P4Error catcher = P4Error.makeJavaPeer(errInstance); try { errorPause(errBuf); } catch (P4ClientException e) { catcher.convertException(e); } } private final void editCALLBACK(int filesysInstance, int errInstance) { P4Error catcher = P4Error.makeJavaPeer(errInstance); try { edit(new FileSys(filesysInstance)); } catch (P4ClientException e) { catcher.convertException(e); } } private final void diffCALLBACK(int fileSys1, int fileSys2, boolean doPage, String diffFlags, int errInstance) { P4Error catcher = P4Error.makeJavaPeer(errInstance); try { diff(new FileSys(fileSys1), new FileSys(fileSys2), doPage, diffFlags); } catch (P4ClientException e) { catcher.convertException(e); } } private final void mergeCALLBACK(int base, int leg1, int leg2, int result, int errInstance) { P4Error catcher = P4Error.makeJavaPeer(errInstance); try { merge(new FileSys(base), new FileSys(leg1), new FileSys(leg2), new FileSys(result)); } catch (P4ClientException e) { catcher.convertException(e); } } private final void helpCALLBACK(String[] helpText) { help(helpText); } private final void finishedCALLBACK() { synchronized(this) { amFinished = true; } try { finished(); } finally { if (releaseAfterFinished) { releaseNativeGlobalRef(); } } } // // NATIVE METHODS // private native long nNewInstance(); private static native void nDeleteInstance(long instance); private static native void nReleaseNativeGlobalRef(long instance); private static native void nEdit(long instance, long fileSysInstance, long errInstance); private static native void nDiff(long instance, long fileSysInstance1, long fileSysInstance2, boolean doPage, String diffFlags, long errInstance); private static native void nMerge(long instance, long fileSysInstanceBase, long fileSysInstanceLeg1, long fileSysInstanceLeg2, long fileSysInstanceResult, long errInstance); }
# | Change | User | Description | Committed | |
---|---|---|---|---|---|
#1 | 4181 | Paul Krause |
Rename //guest/paul_krause/perforce/api/java/wcvm/com/... //guest/paul_krause/perforce/api/java/wcvm/javax/... To //guest/paul_krause/perforce/api/java/wcvm/src-15/... |
||
//guest/paul_krause/perforce/api/java/wcvm/com/perforce/client/api/ClientUser.java | |||||
#5 | 4177 | Paul Krause | parameterize container types - requires tiger or pizza | ||
#4 | 4130 | Paul Krause | Copy Javadoc from Perforce 2003.2 C/C++ API User's Guide. | ||
#3 | 4100 | Paul Krause | fix type problems | ||
#2 | 4089 | Paul Krause | javadoc fixes | ||
#1 | 4073 | Paul Krause | branch com.perforce.api package from michael_bishop | ||
//guest/michael_bishop/P4APIForJava/java/com/perforce/client/api/ClientUser.java | |||||
#1 | 430 | Michael Bishop |
Initial checkin. Seems to work. Not very much testing. Not very much documentation. Some more commenting needs to take place. But, it's there to experiment with. |