package com.perforce.api;
import java.io.*;
import java.util.*;
/*
* Copyright (c) 2001, Perforce Software, All rights reserved.
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the
* "Software"), to deal in the Software without restriction, including
* without limitation the rights to use, copy, modify, merge, publish,
* distribute, sublicense, and/or sell copies of the Software, and to
* permit persons to whom the Software is furnished to do so, subject to
* the following conditions:
*
* The above copyright notice and this permission notice shall be included
* in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
* OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
/**
* Representation of a source control job. This class can be used to determine
* information for a particular p4 job. It can be constructed using the job
* name, but will not contain any additional change information until the <a
* href="#sync()">sync()</a> method is called.
*
* @author <a href="mailto:david@markley.cc">David Markley</a>
* @version $Date: 2002/08/05 $ $Revision: #2 $
*/
public final class Job extends SourceControlObject {
private String name = "new";
private String user = null;
private String modtime_string = "";
private String description = "\tUseless description. This must be changed.";
private int status = OPEN;
private Hashtable fields = new Hashtable();
private static HashDecay jobs = new HashDecay();
private Change[] changes = null;
/** Indicates that the Job is open. */
public final static int OPEN = 1;
/** Indicates that the Job is closed. */
public final static int CLOSED = 2;
/** Indicates that the Job is suspended. */
public final static int SUSPENDED = 4;
/**
* Default no-argument constructor.
*/
public Job(Env env) {
super();
if(null == fields) {
fields = new Hashtable();
}
getCache();
setEnv(env);
}
private static HashDecay setCache() {
if(null == jobs) {
jobs = new HashDecay(300000);
jobs.start();
}
return jobs;
}
public HashDecay getCache() {
return setCache();
}
/**
* Constructor that accepts the job number. This job is not populated with
* the correct information until the sync() method is called on it.
*
* @param name
* Job name
*/
public Job(String name) {
this((Env) null);
this.name = name;
}
public Job() {
this((Env) null);
}
public Job(Env env, String name) {
this(env);
this.name = name;
}
/** Returns the job with the specified name. */
public static Job getJob(String name) {
return getJob(null, name);
}
/** Returns the job with the specified name. */
public static Job getJob(Env env, String name) {
Job j = new Job(name);
if(null != env)
j.setEnv(env);
j.sync();
return j;
}
/** Returns the job's modification time */
public String getModtimeString() {
return modtime_string;
}
/** Sets the job's modification time */
public void setModtimeString(String modtime) {
this.modtime_string = modtime;
}
/**
* Sets the job name for the Job. This invalidates all the other data for
* the Job.
*
* @param name
* Job name
*/
public void setName(String name) {
this.name = name;
user = null;
description = "";
status = OPEN;
}
/**
* Returns the name of this Job.
*/
public String getName() {
return name;
}
/**
* Sets the User that owns this Job.
*
* @param user
* Owning user.
*/
public void setUser(String user) {
this.user = user;
}
/**
* Returns the User that owns this Job.
*/
public String getUser() {
return user;
}
/**
* Sets the description for the job.
*/
public void setDescription(String description) {
String l;
try {
StringBuffer sb = new StringBuffer();
BufferedReader b = new BufferedReader(new StringReader(description));
while(null != (l = b.readLine())) {
sb.append('\t');
sb.append(l.trim());
sb.append('\n');
}
this.description = sb.toString();
} catch(IOException ex) {
this.description = description;
}
}
/**
* Returns the description for the Job. This description includes not only
* the textual description provided by the user, but also the list of
* affected files and how they were affected.
*
* The String returned includes newline characters.
*/
public String getDescription() {
return description;
}
public Enumeration getFieldNames() {
return fields.keys();
}
public void setField(String name, String value) {
fields.put(name.toLowerCase(), value);
}
public String getField(String name) {
return (String) fields.get(name.toLowerCase());
}
public Vector getFileEntries() {
Vector v = new Vector();
Integer pos;
Hashtable h = new Hashtable();
Enumeration en;
FileEntry fent;
changes = getChanges();
for(int i = 0; i < changes.length; i++) {
changes[i].sync();
en = changes[i].getFileEntries().elements();
while(en.hasMoreElements()) {
fent = (FileEntry) en.nextElement();
if(null != (pos = (Integer) h.get(fent.getDepotPath()))) {
if(((FileEntry) v.elementAt(pos.intValue())).getHeadRev() < fent.getHeadRev()) {
v.setElementAt(fent, pos.intValue());
}
} else {
v.addElement(fent);
h.put(fent.getDepotPath(), new Integer(v.size() - 1));
}
}
}
return v;
}
/**
* Sets status for the Job. This can be either OPEN, CLOSED, or SUSPENDED.
*/
public void setStatus(String status) {
if(-1 != status.indexOf("closed")) {
this.status = CLOSED;
} else if(-1 != status.indexOf("suspended")) {
this.status = SUSPENDED;
} else {
this.status = OPEN;
}
}
/**
* Sets status for the Job. This can be either OPEN, CLOSED, or SUSPENDED.
*/
public void setStatus(int status) {
this.status = status;
}
/**
* Returns the status for the Job. This can be either OPEN, CLOSED, or
* SUSPENDED.
*/
public int getStatus() {
return status;
}
public String getStatusName() {
switch(status) {
case SUSPENDED:
return "suspended";
case CLOSED:
return "closed";
default:
return "open";
}
}
/**
* Stores the job information back into perforce.
*
* @deprecated Use {@link #commit() commit()} instead.
*/
public void store() throws CommitException {
this.commit();
}
public void commit() throws CommitException {
StringBuffer sb = new StringBuffer();
String[] cmd = { "p4", "job", "-i" };
String l, key;
int pos;
boolean store_failed = false;
try {
P4Process p = new P4Process(getEnv());
p.exec(cmd);
p.println("Job: " + getName());
p.println("Status: " + getStatusName());
if(null == getUser() && null != getEnv()) {
p.println("User: " + getEnv().getUser());
} else {
p.println("User: " + user);
}
p.println("Description: ");
p.println(getDescription());
Enumeration en = fields.keys();
while(en.hasMoreElements()) {
key = (String) en.nextElement();
p.println(key + ": " + (String) fields.get(key));
}
p.flush();
p.outClose();
while(null != (l = p.readLine())) {
if(l.startsWith("Job ") && (-1 != (pos = l.indexOf("saved")))) {
setName(l.substring(4, pos - 1).trim());
}
if(l.startsWith("Error"))
store_failed = true;
sb.append(l);
sb.append('\n');
}
p.close();
} catch(Exception ex) {
throw new CommitException(ex.getMessage());
}
if(store_failed) {
throw new CommitException(sb.toString());
}
}
/**
* Synchronizes the Job with the correct information from P4, using whatever
* job number has already been set in the Job. After this method is called,
* all the information in the Job is valid.
*/
public void sync() {
sync(name);
}
/**
* Sycnhronizes the Job with the correct information from P4. After this
* method is called, all the information in the Job is valid.
*
* @param number
* Job number
*/
public void sync(String name) {
this.name = name;
int pos;
String l;
String[] cmd = { "p4", "job", "-o", "jobname" };
cmd[3] = name;
String tmpdesc = "";
try {
P4Process p = new P4Process(getEnv());
p.exec(cmd);
p.setRawMode(true);
while(null != (l = p.readLine())) {
if(l.startsWith("info: ")) {
l = l.substring(6);
} else {
continue;
}
if(l.startsWith("#"))
continue;
if(l.startsWith("Job:")) {
name = l.substring(4).trim();
} else if(l.startsWith("Status:")) {
setStatus(l.substring(8).trim());
} else if(l.startsWith("ReportedBy:")) {
user = l.substring(11).trim();
} else if(l.startsWith("ReportedDate:")) {
modtime_string = l.substring(14).trim();
} else if(l.startsWith("Description:")) {
while(null != (l = p.readLine()) && l.startsWith("info: \t")) {
tmpdesc += l.substring(6).trim() + "\n";
}
setDescription(tmpdesc);
} else if(-1 != (pos = l.indexOf(':'))) {
setField(l.substring(0, pos).trim(), l.substring(pos + 1).trim());
}
}
p.close();
} catch(IOException ex) {
Debug.out(Debug.ERROR, ex);
}
}
public static void fix(Env env, String changelist, boolean del, String job) {
Vector jobs = new Vector();
jobs.addElement(job);
fix(env, changelist, del, jobs);
}
public static void fix(Env env, String changelist, boolean del, Vector jobs) {
if(null == jobs)
return;
int i = 0, len = (del) ? 5 : 4;
len += jobs.size();
String cmd[] = new String[len];
String l;
cmd[i++] = "p4";
cmd[i++] = "fix";
cmd[i++] = "-c";
cmd[i++] = changelist;
if(del)
cmd[i++] = "-d";
Enumeration en = jobs.elements();
while(en.hasMoreElements()) {
cmd[i++] = (String) en.nextElement();
}
try {
P4Process p = new P4Process(env);
p.exec(cmd);
while(null != (l = p.readLine())) {
}
p.close();
} catch(IOException ex) {
Debug.out(Debug.ERROR, ex);
}
}
public void removeFix(int changelist) {
}
/**
* Overrides the default toString() method.
*/
public String toString() {
StringBuffer sb = new StringBuffer("Job: ");
sb.append(name);
sb.append("\nUser: ");
sb.append(user);
sb.append("\nDescription:\n");
sb.append(description);
return sb.toString();
}
public static Job[] getJobs(Env env) {
return getJobs(env, (String) null, 0, false, (String[]) null);
}
public static Job[] getJobs(Env env, String jobview, int max, boolean use_integs, String[] files) {
int args = 3, pos = 0;
if(null != jobview)
args += 2;
if(use_integs)
args++;
if(0 < max)
args += 2;
if(null != files)
args += files.length;
String[] cmd = new String[args];
cmd[pos++] = "p4";
cmd[pos++] = "jobs";
cmd[pos++] = "-l";
if(null != jobview) {
cmd[pos++] = "-e";
cmd[pos++] = jobview;
}
if(use_integs) {
cmd[pos++] = "-i";
}
if(0 < max) {
cmd[pos++] = "-m";
cmd[pos++] = String.valueOf(max);
}
if(null != files) {
for(int i = 0; i < files.length; i++) {
cmd[pos++] = files[i];
}
}
Vector v = new Vector();
Job[] jobs;
Job j = null;
StringTokenizer st;
String l, name, user, tmpdesc = "", modtime, state;
try {
P4Process p = new P4Process(env);
p.exec(cmd);
p.setRawMode(true);
while(null != (l = p.readLine())) {
if(l.startsWith("info: \t")) {
tmpdesc += l.substring(7) + "\n";
continue;
} else if(l.trim().equals("info:")) {
continue;
}
if(null != j) {
j.setDescription(tmpdesc);
v.addElement(j);
}
tmpdesc = "";
j = null;
st = new StringTokenizer(l);
if(2 > st.countTokens())
continue;
st.nextToken(); /* Skip 'info:' */
name = st.nextToken();
if(!st.nextToken().equals("on"))
continue;
modtime = st.nextToken();
if(!st.nextToken().equals("by"))
continue;
user = st.nextToken();
state = st.nextToken();
j = new Job(env, name);
j.setModtimeString(modtime);
j.setStatus(state);
j.setUser(user);
}
p.close();
if(null != j) {
j.setDescription(tmpdesc);
v.addElement(j);
}
} catch(IOException ex) {
Debug.out(Debug.ERROR, ex);
}
jobs = new Job[v.size()];
for(int i = 0; i < v.size(); i++) {
jobs[i] = (Job) v.elementAt(i);
}
return jobs;
}
/**
* @return Array of changes that are fixed by this job.
*/
public Change[] getChanges() {
if(null == changes) {
changes = getChangeFixes(getEnv(), getName(), null);
}
return changes;
}
/**
* Returns an array of changes that are fixed by the named job, limited to
* the list of files if specified.
*
* @param env
* Perforce environment to use.
* @param jobname
* Named job to get fixes for.
* @param files
* array of files (including wildcards) used to limit to lookup.
* @return array of changes fixed by the named job.
*/
public static Change[] getChangeFixes(Env env, String jobname, String[] files) {
Vector[] fixes = getFixes(env, jobname, null, files);
Vector vc = fixes[0];
Change[] changes = new Change[vc.size()];
for(int i = 0; i < vc.size(); i++) {
changes[i] = (Change) vc.elementAt(i);
}
return changes;
}
/**
* Returns an array of jobs that fix the specified change, limited to the
* list of files if specified.
*
* @param env
* Perforce environment to use.
* @param change
* Change number (as a <code>String</code>) to lookup jobs
* for.
* @param files
* array of files (including wildcards) used to limit to lookup.
* @return array of jobs that fix the specified change.
*/
public static Job[] getJobFixes(Env env, String change, String[] files) {
Vector[] fixes = getFixes(env, null, change, files);
Vector vj = fixes[1];
Job[] jobs = new Job[vj.size()];
for(int i = 0; i < vj.size(); i++) {
jobs[i] = (Job) vj.elementAt(i);
}
return jobs;
}
/**
* Returns an array of two <code>Vector</code>s. The first
* <code>Vector</code> in the array is filled with the changes fixed. The
* second <code>Vector</code> contains the jobs that fix those changes.
*
* @param env
* Perforce environment to use.
* @param jobname
* Named job to get fixes for.
* @param change
* Change number (as a <code>String</code>) to lookup jobs
* for.
* @param files
* array of files (including wildcards) used to limit to lookup.
* @return an array of two <code>Vector</code>s that contains changes and
* jobs fixed.
*/
private static Vector[] getFixes(Env env, String jobname, String change, String[] files) {
int args = 2, pos = 0;
if(null != jobname) {
args += 2;
jobname = jobname.trim();
}
if(null != change) {
args += 2;
change = change.trim();
}
if(null != files)
args += files.length;
String[] cmd = new String[args];
cmd[pos++] = "p4";
cmd[pos++] = "fixes";
if(null != jobname) {
cmd[pos++] = "-j";
cmd[pos++] = jobname;
}
if(null != change) {
cmd[pos++] = "-c";
cmd[pos++] = change;
}
if(null != files) {
for(int i = 0; i < files.length; i++) {
cmd[pos++] = files[i];
}
}
Vector vc = new Vector();
Vector vj = new Vector();
Change c = null;
Job jb = null;
StringTokenizer st;
String l, jbname, number, user, tmpdesc = "", modtime, state;
try {
P4Process p = new P4Process(env);
p.exec(cmd);
while(null != (l = p.readLine())) {
st = new StringTokenizer(l);
jbname = st.nextToken();
jb = new Job(env, jbname);
vj.addElement(jb);
if(!st.nextToken().equals("fixed"))
continue;
if(!st.nextToken().equals("by"))
continue;
if(!st.nextToken().equals("change"))
continue;
c = new Change(st.nextToken());
c.setEnv(env);
if(!st.nextToken().equals("on"))
continue;
c.setModtimeString(st.nextToken());
if(!st.nextToken().equals("by"))
continue;
c.setClientName(st.nextToken());
if(null != c) {
vc.addElement(c);
c = null;
}
}
p.close();
} catch(IOException ex) {
Debug.out(Debug.ERROR, ex);
}
Vector[] fixes = new Vector[2];
fixes[0] = vc;
fixes[1] = vj;
return fixes;
}
public String toXML() {
StringBuffer sb = new StringBuffer("<job name=\"");
sb.append(getName());
sb.append("\" user=\"");
sb.append(getUser());
sb.append("\" status=\"");
sb.append(getStatusName());
sb.append("\" modtime=\"");
sb.append(getModtimeString());
sb.append("\"><description>");
sb.append(getDescription());
sb.append("<description>");
Enumeration en = fields.keys();
String key;
while(en.hasMoreElements()) {
key = (String) en.nextElement();
sb.append("<field name=\"");
sb.append(key);
sb.append("\" value=\"");
sb.append((String) fields.get(key));
sb.append("\"/>");
}
sb.append("</job>");
return sb.toString();
}
/**
* Used for testing.
*
* @deprecated Actually in use, but this keeps it out of the docs.
*/
public static void main(String[] args) {
String propfile = "/etc/p4.conf";
Env environ = null;
/*
* Debug.setDebugLevel(Debug.VERBOSE);
* Debug.setLogLevel(Debug.LOG_SPLIT);
*/
if(0 < args.length)
propfile = args[0];
try {
environ = new Env(propfile);
} catch(PerforceException ex) {
System.out.println("Could not load properties from " + propfile + ": " + ex);
System.exit(-1);
}
System.out.println(environ);
Job[] jobs = getJobs(environ);
for(int i = 0; i < jobs.length; i++) {
System.out.println(jobs[i].getName() + " [" + jobs[i].getUser() + "]:\n\n" + jobs[i].getDescription());
}
System.out.println("\n---------\n");
Job j = getJob(environ, "job000002");
System.out.println(j.getName() + " [" + j.getUser() + "]:\n\n" + j.getDescription());
System.out.println("\n---------\n");
System.out.println("Job " + j.getName() + " fixes:");
Change[] chngs = j.getChanges();
for(int x = 0; x < chngs.length; x++) {
System.out.println("\t change #" + chngs[x].getNumber());
}
if(0 < chngs.length) {
System.out.println("\n---------\n");
System.out.println("Change " + chngs[0].getNumber() + " is fixed by:");
Job[] jfixes = getJobFixes(environ, String.valueOf(chngs[0].getNumber()), null);
for(int x = 0; x < jfixes.length; x++) {
System.out.println("\t job " + jfixes[x].getName());
}
}
Utils.cleanUp();
}
}