Clover coverage report - DrJava Test Coverage (drjava-20120304-r5456)
Coverage timestamp: Sun Mar 4 2012 03:13:23 CST
file stats: LOC: 814   Methods: 63
NCLOC: 536   Classes: 1
 
 Source file Conditionals Statements Methods TOTAL
ProjectProfile.java 55.3% 78.7% 92.1% 74.2%
coverage coverage
 1    /*BEGIN_COPYRIGHT_BLOCK
 2    *
 3    * Copyright (c) 2001-2010, JavaPLT group at Rice University (drjava@rice.edu)
 4    * All rights reserved.
 5    *
 6    * Redistribution and use in source and binary forms, with or without
 7    * modification, are permitted provided that the following conditions are met:
 8    * * Redistributions of source code must retain the above copyright
 9    * notice, this list of conditions and the following disclaimer.
 10    * * Redistributions in binary form must reproduce the above copyright
 11    * notice, this list of conditions and the following disclaimer in the
 12    * documentation and/or other materials provided with the distribution.
 13    * * Neither the names of DrJava, the JavaPLT group, Rice University, nor the
 14    * names of its contributors may be used to endorse or promote products
 15    * derived from this software without specific prior written permission.
 16    *
 17    * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 18    * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 19    * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 20    * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
 21    * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
 22    * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
 23    * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
 24    * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
 25    * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
 26    * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 27    * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 28    *
 29    * This software is Open Source Initiative approved Open Source Software.
 30    * Open Source Initative Approved is a trademark of the Open Source Initiative.
 31    *
 32    * This file is part of DrJava. Download the current version of this project
 33    * from http://www.drjava.org/ or http://sourceforge.net/projects/drjava/
 34    *
 35    * END_COPYRIGHT_BLOCK*/
 36   
 37    package edu.rice.cs.drjava.project;
 38   
 39    import java.util.ArrayList;
 40    import java.util.LinkedList;
 41    import java.util.List;
 42    import java.util.Map;
 43    import java.util.HashMap;
 44    import java.util.Date;
 45    import java.text.DateFormat;
 46    import java.text.SimpleDateFormat;
 47    import java.util.Locale;
 48    import java.io.*;
 49    import org.w3c.dom.Node;
 50    import edu.rice.cs.plt.tuple.Pair;
 51   
 52    import edu.rice.cs.drjava.config.OptionParser;
 53    import edu.rice.cs.drjava.config.OptionConstants;
 54    import edu.rice.cs.util.AbsRelFile;
 55    import edu.rice.cs.plt.io.IOUtil;
 56    import edu.rice.cs.drjava.Version;
 57    import edu.rice.cs.util.FileOps;
 58    import edu.rice.cs.util.UnexpectedException;
 59    import edu.rice.cs.drjava.model.FileRegion;
 60    import edu.rice.cs.drjava.model.debug.DebugBreakpointData;
 61    import edu.rice.cs.drjava.model.debug.DebugWatchData;
 62    import edu.rice.cs.util.XMLConfig;
 63   
 64    import edu.rice.cs.plt.text.TextUtil;
 65   
 66    import edu.rice.cs.util.Log;
 67   
 68    import static edu.rice.cs.util.StringOps.*;
 69   
 70    /** The internal representation of a project; it is the internal analog of a project file. Includes support for
 71    * writing corresponding project file.
 72    */
 73    public class ProjectProfile implements ProjectFileIR {
 74    static final String MOD_DATE_FORMAT_STRING = "dd-MMM-yyyy HH:mm:ss";
 75    static final DateFormat MOD_DATE_FORMAT =
 76    new SimpleDateFormat(MOD_DATE_FORMAT_STRING, Locale.US);
 77   
 78    /* Private fields */
 79   
 80    private List<DocFile> _sourceFiles = new LinkedList<DocFile>();
 81    private List<DocFile> _auxiliaryFiles = new LinkedList<DocFile>();
 82    private List<DocFile> _excludedFiles = new ArrayList<DocFile>();
 83    private List<String> _collapsedPaths = new ArrayList<String>();
 84   
 85    private File _buildDir = FileOps.NULL_FILE;
 86    private File _workDir = FileOps.NULL_FILE;
 87   
 88    private List<AbsRelFile> _classPathFiles = new ArrayList<AbsRelFile>();
 89   
 90    private String _mainClass = null;
 91   
 92    /** root of project source tree. Invariant: _projectRoot.exists() */
 93    private File _projectRoot; /* Invariant after init: _projectRoot.exists() implying _projectRoot != null. */
 94   
 95    private File _projectFile; /* Invariant after init: _projectFile.getParentFile().exists() implying _projectFile != null */
 96   
 97    private File _createJarFile = FileOps.NULL_FILE;
 98   
 99    private int _createJarFlags = 0;
 100   
 101    private boolean _autoRefreshStatus = false;
 102   
 103    private HashMap<OptionParser<?>,String> _storedPreferences = new HashMap<OptionParser<?>,String>();
 104   
 105    private List<FileRegion> _bookmarks = new ArrayList<FileRegion>();
 106    private List<DebugBreakpointData> _breakpoints = new ArrayList<DebugBreakpointData>();
 107    private List<DebugWatchData> _watches = new ArrayList<DebugWatchData>();
 108   
 109    private String _version = "unknown";
 110   
 111    private String _manifest = null;
 112   
 113    private static Log LOG = new Log("ProjectProfile.txt", false);
 114   
 115    /** Constructs a File for fileName and forwards this call to the main constructor. */
 116  0 public ProjectProfile(String fileName) throws IOException { this(new File(fileName)); }
 117   
 118    /** Creates new ProjectProfiles with specifed project file name and project root that is parent folder of
 119    * the project file. The project file presumably may not exist yet, but its parent folder is assumed to exist.
 120    * Assumes that the File f is not a null reference.
 121    * @throws IOException parent directory of project file does not exist.
 122    */
 123  16 public ProjectProfile(File f) throws IOException {
 124  16 _projectFile = f;
 125  16 _projectRoot = _projectFile.getParentFile();
 126  0 if (! _projectRoot.exists()) throw new IOException("Parent directory of project root " + _projectRoot +
 127    " does not exist");
 128    }
 129   
 130    /* Public getters */
 131   
 132    /** @return an array of the source files in this project. */
 133  28 public DocFile[] getSourceFiles() { return _sourceFiles.toArray(new DocFile[_sourceFiles.size()]); }
 134   
 135    /** @return an array full of all the aux files (project outside source tree) in this project. */
 136  21 public DocFile[] getAuxiliaryFiles() { return _auxiliaryFiles.toArray(new DocFile[_auxiliaryFiles.size()]); }
 137   
 138    /** @return an array chock partially full of most of the excluded files */
 139  5 public DocFile[] getExcludedFiles() { return _excludedFiles.toArray(new DocFile[_excludedFiles.size()]); }
 140   
 141    /** @return project file. */
 142  3 public File getProjectFile() { return _projectFile; }
 143   
 144    /** @return the build directory stored in this project file */
 145  12 public File getBuildDirectory() { return _buildDir; }
 146   
 147    /** @return the working directory stored in this project profile */
 148  12 public File getWorkingDirectory() { return _workDir; }
 149   
 150    /** @return an array of path strings correspond to which folders in the tree should not be shown. Any paths not in
 151    * this list will be expanded when the project is opened.
 152    */
 153  19 public String[] getCollapsedPaths() { return _collapsedPaths.toArray(new String[_collapsedPaths.size()]); }
 154   
 155    /** @return an array full of all the classpath path elements in the classpath for this project file */
 156  20 public Iterable<AbsRelFile> getClassPaths() { return _classPathFiles; }
 157   
 158    /** @return the name of the file that holds the Jar main class associated with this project */
 159  23 public String getMainClass() { return _mainClass; }
 160   
 161    /** @return the file containing the project's main class. */
 162  0 public File getMainClassContainingFile() {
 163  0 DocFile[] possibleContainers = getSourceFiles();
 164   
 165  0 String main = getMainClass();
 166    // TODO: What about language level file extensions? What about Habanero Java extension?
 167  0 if (main.toLowerCase().endsWith(OptionConstants.JAVA_FILE_EXTENSION)) {
 168  0 main = main.substring(0, main.length()-OptionConstants.JAVA_FILE_EXTENSION.length());
 169  0 main = main.replace(File.separatorChar,'.');
 170    }
 171   
 172  0 for (int i = 0; i < possibleContainers.length; i++) {
 173  0 String toMatch = possibleContainers[i].getAbsolutePath();
 174  0 toMatch = toMatch.substring(0, toMatch.lastIndexOf(OptionConstants.JAVA_FILE_EXTENSION));
 175  0 toMatch = toMatch.replace(File.separatorChar,'.');
 176   
 177  0 if(toMatch.endsWith(main))
 178  0 return possibleContainers[i];
 179    }
 180   
 181    //Return a guess at the main class if its not in a source file
 182  0 File toRet = new File(main.replace('.',File.separatorChar) + OptionConstants.JAVA_FILE_EXTENSION);
 183   
 184  0 return toRet;
 185    }
 186   
 187    /** @return the project root directory which must exist. */
 188  6 public File getProjectRoot() { return _projectRoot; }
 189   
 190    /** @return the output file used in the "Create Jar" dialog. */
 191  4 public File getCreateJarFile() { return _createJarFile; }
 192   
 193    /** @return the output file used in the "Create Jar" dialog. */
 194  4 public int getCreateJarFlags() { return _createJarFlags; }
 195   
 196    /** @return an array of the bookmarks in this project. */
 197  3 public FileRegion[] getBookmarks() { return _bookmarks.toArray(new FileRegion[_bookmarks.size()]); }
 198   
 199    /** @return an array of the breakpoints in this project. */
 200  3 public DebugBreakpointData[] getBreakpoints() { return _breakpoints.toArray(new DebugBreakpointData[_breakpoints.size()]); }
 201   
 202    /** @return an array of the watches in this project. */
 203  3 public DebugWatchData[] getWatches() { return _watches.toArray(new DebugWatchData[_watches.size()]); }
 204   
 205  4 public boolean getAutoRefreshStatus() { return _autoRefreshStatus; }
 206   
 207    /** @return the stored preferences. */
 208  4 public Map<OptionParser<?>,String> getPreferencesStoredInProject() {
 209  4 return new HashMap<OptionParser<?>,String>(_storedPreferences);
 210    }
 211   
 212    /** Public setters, modifiers */
 213   
 214  17 public void addSourceFile(DocFile df) { _sourceFiles.add(df); }
 215   
 216  17 public void addSourceFile(DocumentInfoGetter getter) {
 217  17 if (!getter.isUntitled()) {
 218  17 try { addSourceFile(docFileFromGetter(getter)); }
 219  0 catch(IOException e) { throw new UnexpectedException(e); }
 220    }
 221    }
 222   
 223  7 public void addAuxiliaryFile(DocFile df) { _auxiliaryFiles.add(df); }
 224   
 225  7 public void addAuxiliaryFile(DocumentInfoGetter getter) {
 226  7 if (! getter.isUntitled()) {
 227  7 try { addAuxiliaryFile(docFileFromGetter(getter)); }
 228  0 catch(IOException e) { throw new UnexpectedException(e); }
 229    }
 230    }
 231   
 232  0 public void addExcludedFile(DocFile df) { _excludedFiles.add(df); }
 233  0 public void addExcludedFile(File f) { _excludedFiles.add(new DocFile(f)); }
 234   
 235  0 public void addExcludedFile(DocumentInfoGetter getter) {
 236  0 if (! getter.isUntitled()) {
 237  0 try { addExcludedFile(docFileFromGetter(getter)); }
 238  0 catch(IOException e) { throw new UnexpectedException(e); }
 239    }
 240    }
 241   
 242  3 public void addClassPathFile(AbsRelFile cp) {
 243  3 if (cp != null) _classPathFiles.add(cp);
 244    }
 245  3 public void addCollapsedPath(String cp) { if (cp != null) _collapsedPaths.add(cp); }
 246  11 public void setBuildDirectory(File dir) {
 247    // System.err.println("setBuildDirectory(" + dir + ") called");
 248    // removed call to validate to allow build directory that doesn't exist:
 249    // it will be created when necessary
 250  11 _buildDir = dir; // FileOps.validate(dir);
 251    // System.err.println("Vaidated form is: " + _buildDir);
 252    }
 253  13 public void setWorkingDirectory(File dir) { _workDir = FileOps.validate(dir); }
 254  13 public void setMainClass(String main) { _mainClass = main; }
 255  12 public void setSourceFiles(List<DocFile> sf) { _sourceFiles = new LinkedList<DocFile>(sf); }
 256  9 public void setClassPaths(Iterable<? extends AbsRelFile> cpf) {
 257  9 _classPathFiles = new ArrayList<AbsRelFile>();
 258  9 for (AbsRelFile f : cpf) { _classPathFiles.add(f); }
 259    }
 260  9 public void setCollapsedPaths(List<String> cp) { _collapsedPaths = new ArrayList<String>(cp); }
 261  9 public void setAuxiliaryFiles(List<DocFile> af) { _auxiliaryFiles = new LinkedList<DocFile>(af); }
 262  4 public void setExcludedFiles(List<DocFile> ef) { _excludedFiles = new ArrayList<DocFile>(ef); }
 263   
 264    /** Assumes that root.getParentFile != null */
 265  10 public void setProjectRoot(File root) {
 266  10 _projectRoot = root;
 267  10 assert root.getParentFile() != null;
 268    }
 269   
 270  6 public void setCreateJarFile(File createJarFile) { _createJarFile = createJarFile; }
 271  1 public void setCreateJarFlags(int createJarFlags) { _createJarFlags = createJarFlags; }
 272   
 273  5 public void setBookmarks(List<? extends FileRegion> bms) { _bookmarks = new ArrayList<FileRegion>(bms); }
 274  5 public void setBreakpoints(List<? extends DebugBreakpointData> bps) { _breakpoints = new ArrayList<DebugBreakpointData>(bps); }
 275  5 public void setWatches(List<? extends DebugWatchData> ws) { _watches = new ArrayList<DebugWatchData>(ws); }
 276   
 277  4 public void setAutoRefreshStatus(boolean status) { _autoRefreshStatus = status;}
 278   
 279  5 public void setPreferencesStoredInProject(Map<OptionParser<?>,String> sp) {
 280  5 _storedPreferences.clear();
 281  5 _storedPreferences.putAll(sp);
 282    }
 283   
 284    /** Write project file in XML format. */
 285  3 public void write() throws IOException {
 286  3 FileOutputStream fos = null;
 287  3 try {
 288  3 fos = new FileOutputStream(_projectFile);
 289  3 write(fos);
 290    }
 291  3 finally { if (fos != null) fos.close(); }
 292    }
 293   
 294  3 public void write(OutputStream os) throws IOException {
 295  3 XMLConfig xc = new XMLConfig();
 296  3 xc.set("drjava.version", edu.rice.cs.drjava.Version.getVersionString());
 297  3 String path = FileOps.stringMakeRelativeTo(_projectRoot, _projectFile);
 298  3 path = replace(path, File.separator, "/");
 299  3 xc.set("drjava/project.root", path);
 300  3 path = FileOps.stringMakeRelativeTo(_workDir, _projectFile);
 301  3 path = replace(path, File.separator, "/");
 302  3 xc.set("drjava/project.work", path);
 303   
 304  3 if(_manifest != null) {
 305  0 String cleanManifest = TextUtil.xmlEscape(_manifest);
 306  0 xc.set("drjava/project.manifest", cleanManifest);
 307   
 308  0 LOG.log("dirty manifest: " + _manifest);
 309  0 LOG.log("clean manifest: " + cleanManifest);
 310    }
 311   
 312  3 if (_buildDir != null && _buildDir.getPath() != "") {
 313  2 path = FileOps.stringMakeRelativeTo(_buildDir, _projectFile);
 314  2 path = replace(path, File.separator, "/");
 315  2 xc.set("drjava/project.build", path);
 316    }
 317  3 if (_mainClass != null && _mainClass != "") {
 318    /*path = FileOps.stringMakeRelativeTo(_mainClass, _projectFile);
 319    path = replace(path, File.separator, "/");*/
 320  2 xc.set("drjava/project.main", _mainClass);
 321    }
 322  3 xc.set("drjava/project.autorefresh", String.valueOf(_autoRefreshStatus));
 323   
 324  3 if (_createJarFile != null) {
 325  3 path = FileOps.stringMakeRelativeTo(_createJarFile, _createJarFile);
 326  3 path = replace(path, File.separator, "/");
 327  3 xc.set("drjava/project/createjar.file", path);
 328    }
 329  3 if (_createJarFlags != 0) {
 330  0 xc.set("drjava/project/createjar.flags", String.valueOf(_createJarFlags));
 331    }
 332   
 333  3 xc.createNode("drjava/project/source");
 334  3 DocFile active = null;
 335  3 if (!_sourceFiles.isEmpty()) {
 336  3 for(DocFile df: _sourceFiles) {
 337  10 if(df.isActive()) {
 338  2 active = df;
 339  2 break; //Assert that there is only one active document in the project
 340    }
 341    }
 342    // move active document to the front of the list
 343  2 if (active != null) { _sourceFiles.remove(active); _sourceFiles.add(0,active); }
 344  3 for(DocFile df: _sourceFiles) {
 345  12 path = FileOps.stringMakeRelativeTo(df, _projectRoot);
 346  12 path = replace(path, File.separator, "/");
 347  12 Pair<Integer,Integer> pSel = df.getSelection();
 348  12 Pair<Integer,Integer> pScr = df.getScroll();
 349  12 String s = MOD_DATE_FORMAT.format(new Date(df.lastModified()));
 350   
 351  12 Node f = xc.createNode("drjava/project/source/file", null, false);
 352  12 xc.set(".name", path, f, true);
 353  12 xc.set(".timestamp", s, f, true);
 354  12 String pkg = df.getPackage();
 355  12 xc.set(".package", (pkg != null)?pkg:"", f, true);
 356  12 xc.set("select.from", String.valueOf((pSel != null)?pSel.first():0), f, true);
 357  12 xc.set("select.to", String.valueOf((pSel != null)?pSel.second():0), f, true);
 358  12 xc.set("scroll.column", String.valueOf((pScr != null)?pScr.first():0), f, true);
 359  12 xc.set("scroll.row", String.valueOf((pScr != null)?pScr.second():0), f, true);
 360  2 if (df==active) xc.set(".active", "true", f, true);
 361    }
 362    }
 363  3 xc.createNode("drjava/project/included");
 364  3 if (!_auxiliaryFiles.isEmpty()) {
 365  3 if (active == null) {
 366  1 for(DocFile df: _auxiliaryFiles) {
 367  1 if(df.isActive()) {
 368  1 active = df;
 369  1 break; //Assert that there is only one active document in the project
 370    }
 371    }
 372    // move active document to the front of the list
 373  1 if (active != null) { _auxiliaryFiles.remove(active); _auxiliaryFiles.add(0,active); }
 374    }
 375  3 for(DocFile df: _auxiliaryFiles) {
 376  5 path = df.getAbsolutePath();
 377  5 path = replace(path, File.separator, "/");
 378  5 Pair<Integer,Integer> pSel = df.getSelection();
 379  5 Pair<Integer,Integer> pScr = df.getScroll();
 380  5 String s = MOD_DATE_FORMAT.format(new Date(df.lastModified()));
 381   
 382  5 Node f = xc.createNode("drjava/project/included/file", null, false);
 383  5 xc.set(".name", path, f, true);
 384  5 xc.set(".timestamp", s, f, true);
 385  5 String pkg = df.getPackage();
 386  5 xc.set(".package", (pkg != null)?pkg:"", f, true);
 387  5 xc.set("select.from", String.valueOf((pSel != null)?pSel.first():0), f, true);
 388  5 xc.set("select.to", String.valueOf((pSel != null)?pSel.second():0), f, true);
 389  5 xc.set("scroll.column", String.valueOf((pScr != null)?pScr.first():0), f, true);
 390  5 xc.set("scroll.row", String.valueOf((pScr != null)?pScr.second():0), f, true);
 391  1 if (df==active) { xc.set(".active", "true", f, true);
 392    }
 393    }
 394    }
 395   
 396  3 xc.createNode("drjava/project/excluded");
 397  3 if (!_excludedFiles.isEmpty()) {
 398  0 if (active == null) {
 399  0 for(DocFile df: _excludedFiles) {
 400  0 if(df.isActive()) {
 401  0 active = df;
 402  0 break; //Assert that there is only one active document in the project
 403    }
 404    }
 405    // move active document to the front of the list
 406  0 if (active != null) { _excludedFiles.remove(active); _excludedFiles.add(0,active); }
 407    }
 408  0 for(DocFile df: _excludedFiles) {
 409  0 path = df.getAbsolutePath();
 410  0 path = replace(path, File.separator, "/");
 411  0 Pair<Integer,Integer> pSel = df.getSelection();
 412  0 Pair<Integer,Integer> pScr = df.getScroll();
 413  0 String s = MOD_DATE_FORMAT.format(new Date(df.lastModified()));
 414   
 415  0 Node f = xc.createNode("drjava/project/excluded/file", null, false);
 416  0 xc.set(".name", path, f, true);
 417  0 xc.set(".timestamp", s, f, true);
 418  0 String pkg = df.getPackage();
 419  0 xc.set(".package", (pkg != null)?pkg:"", f, true);
 420  0 xc.set("select.from", String.valueOf((pSel != null)?pSel.first():0), f, true);
 421  0 xc.set("select.to", String.valueOf((pSel != null)?pSel.second():0), f, true);
 422  0 xc.set("scroll.column", String.valueOf((pScr != null)?pScr.first():0), f, true);
 423  0 xc.set("scroll.row", String.valueOf((pScr != null)?pScr.second():0), f, true);
 424  0 if (df==active) { xc.set(".active", "true", f, true);
 425    }
 426    }
 427    }
 428   
 429  3 xc.createNode("drjava/project/collapsed");
 430  3 if (!_collapsedPaths.isEmpty()) {
 431  2 for(String s: _collapsedPaths) {
 432  2 Node f = xc.createNode("drjava/project/collapsed/path", null, false);
 433  2 xc.set(".name", s, f, true);
 434    }
 435    }
 436  3 xc.createNode("drjava/project/classpath");
 437  3 if (!_classPathFiles.isEmpty()) {
 438  2 for(AbsRelFile cp: _classPathFiles) {
 439  2 path = cp.keepAbsolute()?cp.getAbsolutePath():FileOps.stringMakeRelativeTo(cp, _projectRoot);
 440  2 path = replace(path, File.separator, "/");
 441  2 Node f = xc.createNode("drjava/project/classpath/file", null, false);
 442  2 xc.set(".name", path, f, true);
 443  2 xc.set(".absolute", String.valueOf(cp.keepAbsolute()), f, true);
 444    }
 445    }
 446  3 xc.createNode("drjava/project/breakpoints");
 447  3 if (!_breakpoints.isEmpty()) {
 448  0 for(DebugBreakpointData bp: _breakpoints) {
 449  0 Node f = xc.createNode("drjava/project/breakpoints/breakpoint", null, false);
 450  0 path = FileOps.stringMakeRelativeTo(bp.getFile(), _projectRoot);
 451  0 path = replace(path, File.separator, "/");
 452  0 xc.set(".file", path, f, true);
 453  0 xc.set(".line", String.valueOf(bp.getLineNumber()), f, true);
 454  0 xc.set(".enabled", String.valueOf(bp.isEnabled()), f, true);
 455    }
 456    }
 457  3 xc.createNode("drjava/project/watches");
 458  3 if (!_watches.isEmpty()) {
 459  0 for(DebugWatchData w: _watches) {
 460  0 Node f = xc.createNode("drjava/project/watches/watch", null, false);
 461  0 xc.set(".name", w.getName(), f, true);
 462    }
 463    }
 464  3 xc.createNode("drjava/project/bookmarks");
 465  3 if (!_bookmarks.isEmpty()) {
 466  0 for (FileRegion bm: _bookmarks) {
 467  0 Node n = xc.createNode("drjava/project/bookmarks/bookmark", null, false);
 468  0 File file = bm.getFile();
 469  0 path = FileOps.stringMakeRelativeTo(file, _projectRoot);
 470  0 path = replace(path, File.separator, "/");
 471  0 xc.set(".file", path, n, true);
 472  0 xc.set(".from", String.valueOf(bm.getStartOffset()), n, true);
 473  0 xc.set(".to", String.valueOf(bm.getEndOffset()), n, true);
 474    }
 475    }
 476  3 xc.createNode("drjava/project/preferences");
 477  3 if (!_storedPreferences.isEmpty()) {
 478  0 for(Map.Entry<OptionParser<?>,String> e: _storedPreferences.entrySet()) {
 479  0 Node n = xc.createNode("drjava/project/preferences/preference", null, false);
 480  0 xc.set(".name", TextUtil.xmlEscape(e.getKey().getName()), n, true);
 481  0 xc.set(".value", TextUtil.xmlEscape(e.getValue()), n, true);
 482    }
 483    }
 484  3 xc.save(os);
 485    }
 486   
 487    /** This method writes what information has been passed to this builder so far to disk in s-expression format. */
 488  1 public void writeOld() throws IOException {
 489  1 FileWriter fw = null;
 490  1 try {
 491  1 fw = new FileWriter(_projectFile);
 492  1 writeOld(fw);
 493    }
 494  1 finally { if (fw != null) fw.close(); }
 495    }
 496   
 497  4 public String toString() {
 498  4 try {
 499  4 StringWriter w = new StringWriter();
 500  4 writeOld(w);
 501  4 return w.toString();
 502    }
 503  0 catch(IOException e) { return e.toString(); }
 504    }
 505   
 506  5 public void writeOld(Writer fw) throws IOException {
 507  5 assert (_projectRoot != null);
 508    // write opening comment line
 509  5 fw.write(";; DrJava project file, written by build " + Version.getVersionString());
 510  5 fw.write("\n;; files in the source tree are relative to: " + _projectRoot.getCanonicalPath());
 511  5 fw.write("\n;; other files with relative paths are rooted at (the parent of) this project file");
 512   
 513    // write the project root
 514    /* In the new project file form, this property has been renamed "proj-root-and-base" (instead of "proj-root") to
 515    * indicate that the project root now serves as the base for source file path names. */
 516   
 517  5 fw.write("\n(proj-root-and-base");
 518    // Utilities.show("Writing project root = " + _projRoot);
 519  5 fw.write("\n" + encodeFileRelative(_projectRoot, " ", _projectFile));
 520  5 fw.write(")");
 521   
 522    //write the project manifest
 523  5 if(_manifest != null){
 524  0 fw.write("\n(proj-manifest");
 525  0 fw.write("\n" + _manifest);
 526  0 fw.write(")");
 527    }
 528   
 529    // write source files
 530    /* This property has been renamed "source-files" (instead of "source") so that old versions of DrJava will not
 531    * recognize it. In the new project file format, source files are relative to the project root, not the parent
 532    * of the project file. */
 533  5 if (!_sourceFiles.isEmpty()) {
 534  5 fw.write("\n(source-files");
 535  5 DocFile active = null;
 536  5 for(DocFile df: _sourceFiles) {
 537  9 if(df.isActive()) {
 538  4 active = df;
 539  4 fw.write("\n" + encodeDocFileRelative(df, " "));
 540  4 break; //Assert that there is only one active document in the project
 541    }
 542    }
 543  5 for(DocFile df: _sourceFiles) {
 544  23 if(df != active)
 545  19 fw.write("\n" + encodeDocFileRelative(df, " "));
 546    }
 547  5 fw.write(")"); // close the source expression
 548    }
 549  0 else fw.write("\n;; no source files");
 550   
 551    // write aux files
 552  5 if (!_auxiliaryFiles.isEmpty()) {
 553  5 fw.write("\n(auxiliary");
 554  8 for(DocFile df: _auxiliaryFiles) { fw.write("\n" + encodeDocFileAbsolute(df, " ")); }
 555  5 fw.write(")"); // close the auxiliary expression
 556    }
 557  0 else fw.write("\n;; no aux files");
 558   
 559    // write collapsed paths
 560  5 if (!_collapsedPaths.isEmpty()) {
 561  4 fw.write("\n(collapsed");
 562  4 for(String s: _collapsedPaths) {
 563  5 fw.write("\n (path " + convertToLiteral(s) + ")");
 564    }
 565  4 fw.write(")"); // close the collapsed expression
 566    }
 567  1 else fw.write("\n;; no collapsed branches");
 568   
 569    // write classpaths
 570  5 if (!_classPathFiles.isEmpty()) {
 571  4 fw.write("\n(classpaths");
 572  4 for (AbsRelFile f: _classPathFiles) {
 573  5 fw.write("\n" + encodeFileAbsolute(f, " "));
 574    }
 575  4 fw.write(")"); // close the classpaths expression
 576    }
 577  1 else fw.write("\n;; no classpaths files");
 578   
 579    // write the build directory
 580  5 if (_buildDir != null && _buildDir.getPath() != "") {
 581  4 fw.write("\n(build-dir");
 582  4 fw.write("\n" + encodeFileRelative(_buildDir, " ", _projectFile));
 583  4 fw.write(")");
 584    }
 585  1 else fw.write("\n;; no build directory");
 586   
 587    // write the working directory
 588  5 if (_workDir.getPath() != "") {
 589  5 fw.write("\n(work-dir");
 590  5 fw.write("\n" + encodeFileRelative(_workDir, " ", _projectFile));
 591  5 fw.write(")");
 592    }
 593  0 else fw.write("\n;; no working directory");
 594   
 595    // write the main class
 596  5 if (_mainClass != null) {
 597  4 fw.write("\n;; rooted at the (parent of the) project file");
 598  4 fw.write("\n(main-class");
 599  4 fw.write("\n" + " " + getMainClass() );
 600  4 fw.write(")");
 601    }
 602  1 else fw.write("\n;; no main class");
 603   
 604    // write the create jar file
 605  5 if (_createJarFile != null) {
 606  5 fw.write("\n(create-jar-file");
 607  5 fw.write("\n" + encodeFileRelative(_createJarFile, " ", _projectFile));
 608  5 fw.write(")");
 609    }
 610  0 else fw.write("\n;; no create jar file");
 611   
 612    // write the create jar flags
 613  5 if (_createJarFlags != 0) {
 614  1 fw.write("\n(create-jar-flags " + _createJarFlags + ")");
 615    }
 616  4 else fw.write("\n;; no create jar flags");
 617   
 618    // write breakpoints
 619  5 if (!_breakpoints.isEmpty()) {
 620  1 fw.write("\n(breakpoints");
 621  3 for(DebugBreakpointData bp: _breakpoints) { fw.write("\n" + encodeBreakpointRelative(bp, " ")); }
 622  1 fw.write(")"); // close the breakpoints expression
 623    }
 624  4 else fw.write("\n;; no breakpoints");
 625   
 626    // write watches
 627  5 if (!_watches.isEmpty()) {
 628  1 fw.write("\n(watches");
 629  2 for(DebugWatchData w: _watches) { fw.write("\n" + encodeWatch(w, " ")); }
 630  1 fw.write(")"); // close the watches expression
 631    }
 632  4 else fw.write("\n;; no watches");
 633   
 634    // write bookmarks
 635  5 if (!_bookmarks.isEmpty()) {
 636  1 fw.write("\n(bookmarks");
 637  2 for(FileRegion bm: _bookmarks) { fw.write("\n" + encodeBookmarkRelative(bm, " ")); }
 638  1 fw.write(")"); // close the bookmarks expression
 639    }
 640  4 else fw.write("\n;; no bookmarks");
 641   
 642  5 fw.close();
 643    }
 644   
 645   
 646    /* Private Methods */
 647   
 648    /** @param g The getter that can get all the info needed to make the document file
 649    * @return the document that contains the information retrieved from the getter
 650    */
 651  24 private DocFile docFileFromGetter(DocumentInfoGetter g) throws IOException {
 652  24 return new DocFile(g.getFile().getCanonicalPath(), g.getSelection(), g.getScroll(), g.isActive(), g.getPackage());
 653    }
 654   
 655   
 656    /** This encodes a normal file relative to File base. None of the special tags are added.
 657    * @param f the file to encode
 658    * @param prefix the indent level to place the s-expression at
 659    * @param base Directory to be made relative to
 660    * @return the s-expression syntax to describe the given file.
 661    */
 662  19 private String encodeFileRelative(File f, String prefix, File base) throws IOException {
 663  19 String path = FileOps.stringMakeRelativeTo(f, base);
 664  19 path = replace(path, File.separator, "/");
 665  19 return prefix + "(file (name " + convertToLiteral(path) + "))";
 666    }
 667   
 668    // Not currently used.
 669    // /** This encodes a normal file relative to _projectRoot. None of the special tags are added. */
 670    // private String encodeFileRelative(File f, String prefix) throws IOException {
 671    // return encodeFileRelative(f, prefix, _projectRoot);
 672    // }
 673   
 674    /** This encodes a normal file with its canonical path. None of the special tags are added.
 675    * @param f the file to encode
 676    * @param prefix the indent level to place the s-expression at
 677    * @return the s-expression syntax to describe the given file.
 678    */
 679  5 private String encodeFileAbsolute(File f, String prefix) throws IOException {
 680  5 String path = f.getCanonicalPath();
 681  5 path = replace(path,File.separator, "/");
 682  5 return prefix + "(file (name " + convertToLiteral(path) + "))";
 683    }
 684   
 685    /** This encodes a docfile, adding all the special tags that store document-specific information.
 686    * @param df the doc file to encode
 687    * @param prefix the indent level to place the s-expression at
 688    * @param relative whether this file should be made relative to _projectRoot
 689    * @return the s-expression syntax to describe the given docfile.
 690    */
 691  31 private String encodeDocFile(DocFile df, String prefix, boolean relative) throws IOException {
 692  31 String ret = "";
 693  31 String path;
 694  23 if (relative) path = FileOps.stringMakeRelativeTo(df, _projectRoot);
 695  8 else path = IOUtil.attemptCanonicalFile(df).getPath();
 696   
 697  31 path = replace(path, File.separator, "/");
 698  31 ret += prefix + "(file (name " + convertToLiteral(path) + ")";
 699   
 700  31 Pair<Integer,Integer> p1 = df.getSelection();
 701  31 Pair<Integer,Integer> p2 = df.getScroll();
 702    //boolean active = false; //df.isActive();
 703  31 long modDate = df.lastModified();
 704    // Add prefix to the next line if any tags exist
 705  31 if (p1 != null || p2 != null /*|| active */) ret += "\n" + prefix + " ";
 706   
 707    // The next three tags go on the same line (if they exist)
 708  31 if (p1 != null) ret += "(select " + p1.first() + " " + p1.second() + ")";
 709   
 710  31 if (p2 != null) ret += "(scroll " + p2.first() + " " + p2.second() + ")";
 711   
 712  31 if (modDate > 0) {
 713  10 String s = MOD_DATE_FORMAT.format(new Date(modDate));
 714  10 ret += "(mod-date " + convertToLiteral(s) + ")";
 715    }
 716   
 717    //if (active) ret += "(active)"; //Active document is first on list
 718   
 719    // the next tag goes on the next line if at all
 720  31 String pack = df.getPackage();
 721  31 if (pack != null) {
 722  31 ret += "\n" + prefix + " "; // add prefix
 723  31 ret += "(package " + convertToLiteral(pack) + ")";
 724    }
 725   
 726  31 ret += ")"; // close the file expression
 727   
 728  31 return ret;
 729    }
 730    /** Encodes a doc file relative to _projectRoot.
 731    * @param df the DocFile to encode
 732    * @param prefix the indent level
 733    */
 734  23 private String encodeDocFileRelative(DocFile df, String prefix) throws IOException {
 735  23 return encodeDocFile(df, prefix, true);
 736    }
 737  8 private String encodeDocFileAbsolute(DocFile df, String prefix) throws IOException {
 738  8 return encodeDocFile(df, prefix, false);
 739    }
 740   
 741    /** This encodes a breakpoint relative to _projectRoot.
 742    * @param bp the breakpoint to encode
 743    * @param prefix the indent level to place the s-expression at
 744    * @return the s-expression syntax to describe the given breakpoint.
 745    */
 746  3 private String encodeBreakpointRelative(DebugBreakpointData bp, String prefix) throws IOException {
 747  3 String ret = "";
 748  3 String path = FileOps.stringMakeRelativeTo(bp.getFile(), _projectRoot);
 749   
 750  3 path = replace(path,File.separator,"/");
 751  3 ret += prefix + "(breakpoint (name " + convertToLiteral(path) + ")";
 752   
 753  3 int lineNumber = bp.getLineNumber();
 754  3 ret += "\n" + prefix + " ";
 755  3 ret += "(line " + lineNumber + ")";
 756  3 if (bp.isEnabled()) ret += "(enabled)";
 757  3 ret += ")"; // close the breakpoint expression
 758   
 759  3 return ret;
 760    }
 761   
 762    /** This encodes a watch.
 763    * @param w the watch to encode
 764    * @param prefix the indent level to place the s-expression at
 765    * @return the s-expression syntax to describe the given watch.
 766    */
 767  2 private String encodeWatch(DebugWatchData w, String prefix) throws IOException {
 768  2 String ret = "";
 769   
 770  2 ret += prefix + "(watch " + convertToLiteral(w.getName()) + ")";
 771   
 772  2 return ret;
 773    }
 774   
 775    /** This encodes a bookmark relative to _projectRoot.
 776    * @param bm the bookmark to encode
 777    * @param prefix the indent level to place the s-expression at
 778    * @return the s-expression syntax to describe the given breakpoint.
 779    */
 780  2 private String encodeBookmarkRelative(FileRegion bm, String prefix) throws IOException {
 781  2 String ret = "";
 782  2 String path = FileOps.stringMakeRelativeTo(bm.getFile(), _projectRoot);
 783   
 784  2 path = replace(path,File.separator,"/");
 785  2 ret += prefix + "(bookmark (name " + convertToLiteral(path) + ")";
 786   
 787  2 int startOffset = bm.getStartOffset();
 788  2 int endOffset = bm.getEndOffset();
 789  2 ret += "\n" + prefix + " ";
 790  2 ret += "(start " + startOffset + ")";
 791  2 ret += "(end " + endOffset + ")";
 792  2 ret += ")"; // close the bookmarks expression
 793   
 794  2 return ret;
 795    }
 796   
 797  8 public String getDrJavaVersion(){
 798  8 return _version;
 799    }
 800   
 801  12 public void setDrJavaVersion(String version){
 802  12 _version = version;
 803    }
 804   
 805    /** Accessor for manifest attribute/ */
 806  4 public String getCustomManifest(){
 807  4 return _manifest;
 808    }
 809   
 810    /** Mutator for manifest attribute. */
 811  1 public void setCustomManifest(String manifest){
 812  1 _manifest = manifest;
 813    }
 814    }