package org.acm.seguin.version; import java.io.File; import java.io.FileNotFoundException; import java.io.IOException; import java.rmi.UnexpectedException; import java.util.Enumeration; import org.acm.seguin.awt.ExceptionPrinter; import org.acm.seguin.util.FileSettings; import com.perforce.api.Change; import com.perforce.api.CommitException; import com.perforce.api.Env; import com.perforce.api.FileEntry; /** * A front-end to P4Package, Perforce's Java API. Download it from <pre> * http://public.perforce.com/public/perforce/api/java/p4package/dist/p4.jar * </pre> * * <p> Perforce operations are performed on change lists, not files. * Multiple files can be included in any change list. Submitting the * change list to the depot works atomically: either all the files are * updated in the depot, or none of them are. (In P4 lingo, this is * called an atomic change transaction). Change lists can be used to * keep files together that have a common purpose. * * <p> This class automatically manages an active change list, * creating a new one as needed to perform file operations. Clients * can act on files with no need to track change lists, allowing them * to be written generically across source code control systems. * * @author Paul Krause <pkrause@soundbite.com> * @version $Id: //guest/paul_krause/perforce/integrations/jrefactory/org/acm/seguin/version/Perforce.java#4 $ * @see <a href="http://public.perforce.com/public/perforce/api/index.html">Perforce Public Depot</a> */ public class Perforce implements VersionControl { public static final int LOCK_NEVER = 0; public static final int LOCK_AT_CHECKIN = 1; public static final int LOCK_AT_CHECKOUT = 2; private Env p4Env = new Env(); /** The active change list */ private Change changeList; /** Our locking policy */ private int lockingPolicy; /** * Creates a new <code>Perforce</code> instance * configured using the settings from the file * <var>HOME</var><samp>/.Refactory/p4.settings</samp> * (where <var>HOME</var> is either the value of the * <code>jrefactory.home</code> system property, or the * <code>user.home</code> system property). */ public Perforce () { this(FileSettings.getSettings("Refactory", "p4")); } /** * Creates a new <code>Perforce</code> instance. * * @param bundle Config <code>FileSettings</code> */ public Perforce (FileSettings bundle) { final Enumeration keys = bundle.getKeys(); while (keys.hasMoreElements()) { final String key = (String)keys.nextElement(); switch (key.charAt(3)) { case 'c': if (key.equals("p4.client")) p4Env.setClient(bundle.getString(key)); break; case 'e': if (key.equals("p4.executable")) p4Env.setExecutable(bundle.getString(key)); break; case 'l': if (key.equals("p4.lockingpolicy")) setLockingPolicy(bundle.getString(key)); break; case 'p': if (key.equals("p4.port")) p4Env.setPort(bundle.getString(key)); else if (key.equals("p4.password")) p4Env.setPassword(bundle.getString(key)); break; case 's': if (key.equals("p4.systemdrive")) p4Env.setSystemDrive(bundle.getString(key)); else if (key.equals("p4.systemroot")) p4Env.setSystemRoot(bundle.getString(key)); break; case 'u': if (key.equals("p4.user")) p4Env.setUser(bundle.getString(key)); break; default: final String msg = "Ignoring " + key + "=" + bundle.getString(key); System.out.println(msg); } } } /** * Sets the locking policy to: <ul> * <li> lock at checkin if * <code>policy</code> contains the string * <code>"checkin"</code>, ignoring case; * <li> lock at checkout if * <code>policy</code> contains the string * <code>"checkout"</code>, ignoring case; * <li> lock never otherwise. </ul> * * @param policy a locking policy <code>String</code> */ public void setLockingPolicy (String policy) { if (policy == null) { lockingPolicy = LOCK_NEVER; return; } // ignore everything before up to first c (or C) int c = policy.indexOf('c'); if (c == -1) c = policy.indexOf('C'); if (policy.regionMatches(true, c, "CHECKIN", 0, 7)) { lockingPolicy = LOCK_AT_CHECKIN; } else if (policy.regionMatches(true, c, "CHECKOUT", 0, 8)) { lockingPolicy = LOCK_AT_CHECKOUT; } else { lockingPolicy = LOCK_NEVER; } } /** * If <code>policy</code> is a valid locking policy, changes locking * policy to it. Otherwise, the policy is <code>LOCK_NEVER</code>. * * @param policy one of <code>LOCK_NEVER</code>, * <code>LOCK_AT_CHECKIN</code>, or <code>LOCK_AT_CHECKOUT</code>. */ public void setLockingPolicy (int policy) { switch (policy) { case LOCK_AT_CHECKIN: case LOCK_AT_CHECKOUT: lockingPolicy = policy; break; default: lockingPolicy = LOCK_NEVER; } } private void initChangeList () throws CommitException { changeList = new Change(p4Env); changeList.setDescription("Refactoring with jRefactory"); changeList.commit(); } /** * Syncs the active change list, if there is one, otherwise creates * a new one. If the synced change list appears to already have * been submitted, throws it away and replaces it with a new one. * * @exception CommitException if unable to create a new one */ public void refreshChangeList () throws CommitException { if (changeList == null) { initChangeList(); return; } changeList.sync(); if (changeList.getStatus() == Change.SUBMITTED) { initChangeList(); } else if (changeList.getDescription().endsWith("no such change")) { // Change was renumbered on submission: object is full of bogus data. initChangeList(); } } /** * Adds a new file to the active change list. * * @param path Full pathname <code>String</code> * @exception CommitException if unable to get a change list * @exception IOException if an I/O error occurs * @see FileEntry#openForAdd */ public void openForAdd (String path) throws CommitException, IOException { refreshChangeList(); try { FileEntry.openForAdd(p4Env, path, changeList); } catch (IOException e) { throw e; } catch (Exception e) { final String msg = "Perforce failed while adding "+ path; System.out.println(msg); throw new UnexpectedException(msg, e); } } /** * Adds a new file to the active change list. * * @param f Readable and writable <code>File</code> * @exception CommitException if unable to get a change list * @exception IOException if an I/O error occurs * @see FileEntry#openForAdd */ public void openForAdd (File f) throws CommitException, IOException { openForAdd(f.getCanonicalPath()); } /** * Adds a new file to the active change list. * * @param fe P4 <code>FileEntry</code> * @exception CommitException if unable to get a change list * @exception IOException if an I/O error occurs * @see FileEntry#openForAdd */ public void openForAdd (FileEntry fe) throws CommitException, IOException { refreshChangeList(); openForAddNow(fe); } // like openForAdd, but doesn't refresh change list private void openForAddNow (FileEntry fe) throws IOException { try { fe.openForAdd(changeList); } catch (IOException e) { throw e; } catch (Exception e) { final String msg = "Perforce failed while adding "+ fe; System.out.println(msg); throw new UnexpectedException(msg, e); } } /** * Opens a file with the active change list. * * @param path Full pathname <code>String</code> * @param force if <code>true</code>, open even if out of sync * @param lock if <code>true</code>, lock the file * @exception CommitException if an error occurs * @exception IOException if an I/O error occurs * @see FileEntry#openForEdit */ public void openForEdit (String path, boolean force, boolean lock) throws CommitException, IOException { refreshChangeList(); try { FileEntry fe = new FileEntry(p4Env, path); fe.sync(); if (fe.getDepotPath() == null) { final String msg = "File " + path + " does not exist in depot," + " or is not part of client " + p4Env.getClient() + "'s client-view."; throw new FileNotFoundException(msg); } fe.openForEdit(force, lock, changeList); } catch (IOException e) { throw e; } catch (Exception e) { final String msg = "Perforce failed while opening "+ path; System.out.println(msg); throw new UnexpectedException(msg, e); } } /** * Opens a file with the active change list. * * @param f A readable, writable <code>File</code> * @param force if <code>true</code>, open even if out of sync * @param lock if <code>true</code>, lock the file * @exception CommitException if an error occurs * @exception IOException if an I/O error occurs * @see FileEntry#openForEdit */ public void openForEdit (File f, boolean force, boolean lock) throws CommitException, IOException { openForEdit(f.getCanonicalPath(), force, lock); } /** * Opens a file with the active change list. * * @param fe A <code>FileEntry</code> * @param force if <code>true</code>, open even if out of sync * @param lock if <code>true</code>, lock the file * @exception CommitException if an error occurs * @exception IOException if an I/O error occurs * @see FileEntry#openForEdit */ public void openForEdit (FileEntry fe, boolean force, boolean lock) throws CommitException, IOException { refreshChangeList(); try { fe.openForEdit(force, lock, changeList); } catch (IOException e) { throw e; } catch (Exception e) { final String msg = "Perforce failed while opening "+ fe; System.out.println(msg); throw new UnexpectedException(msg, e); } } /** * Locks file on the active change list. * * @param fe a <code>FileEntry</code> value for an open file * @exception CommitException if an error occurs with the change list * @exception IOException if an error occurs setting the lock */ public void obtainLock (FileEntry fe) throws CommitException, IOException { refreshChangeList(); fe.setHeadChange(changeList.getNumber()); obtainLockNow(fe); } // like obtainLock, but doesn't touch changeList private void obtainLockNow (FileEntry fe) throws IOException { try { fe.obtainLock(); } catch (IOException e) { throw e; } catch (Exception e) { final String msg = "Perforce failed to lock "+ fe; System.out.println(msg); throw new UnexpectedException(msg, e); } } // implementation of org.acm.seguin.version.VersionControl interface /** * Opens a new file using the active change list. * * @param path Full filename */ public void add(String path) { try { openForAdd(path); } catch (CommitException e) { ExceptionPrinter.print(e, false); } catch (IOException e) { ExceptionPrinter.print(e, false); } } /** * Indicates whether a file is under Perforce. * * @param path Full filename * @return true if the file is under version control */ public boolean contains(String path) { final FileEntry fe = new FileEntry(p4Env, path); fe.sync(); return (fe.getDepotPath() != null); } /** * Adds a file to the active change list. * Locks it if the policy is to lock files at checkin. * Does <em>not</em> commit. * * @param path Full filename */ public void checkIn(String path) { try { final FileEntry fe = new FileEntry(p4Env, path); fe.sync(); refreshChangeList(); if (fe.getDepotPath() == null) openForAddNow(fe); else fe.setHeadChange(changeList.getNumber()); if (lockingPolicy == LOCK_AT_CHECKIN) obtainLockNow(fe); } catch (CommitException e) { ExceptionPrinter.print(e, false); } catch (IOException e) { ExceptionPrinter.print(e, false); } } /** * Checks a file out of Perforce. * Locks it if the policy is to lock at checkout. * * @param path Full filename */ public void checkOut (String path) { try { final boolean force = true; final boolean lock = (lockingPolicy == LOCK_AT_CHECKOUT); openForEdit(path, force, lock); } catch (CommitException e) { ExceptionPrinter.print(e, false); } catch (IOException e) { ExceptionPrinter.print(e, false); } } }// Perforce
# | Change | User | Description | Committed | |
---|---|---|---|---|---|
#4 | 4061 | Paul Krause | integrate SourceSafe.java 1.5 and VersionControlFactory.java 1.3 from latest CVS | ||
#3 | 4058 | Paul Krause | revert to ExceptionPrinter.java 1.2 | ||
#2 | 1717 | Paul Krause |
bring org.acm.seguin.version.Perforce up to date add the rest of the package to public depot |
||
#1 | 1715 | Paul Krause | move jrefactory into integrations directory | ||
//guest/paul_krause/perforce/api/java/jrefactory/org/acm/seguin/version/Perforce.java | |||||
#1 | 1713 | Paul Krause | reorg java workspace into multiple projects | ||
//guest/paul_krause/perforce/api/java/org/acm/seguin/version/Perforce.java | |||||
#1 | 764 | Paul Krause | checkpoint - first working compile |