Skip to content

Commit 7636e26

Browse files
author
mgricken
committed
Added option to overwrite read-only source and project files.
Changed DrJava so it actually terminates, instead of hangs, if it is started with the -help argument. Added checks to make sure all references in interactionEnded are non-null, in an attempt to fix bug [ 1938268 ]. git-svn-id: file:///tmp/test-svn/trunk@4451 fe72c1cf-3628-48e9-8b72-1c46755d3cff
1 parent aff4ca7 commit 7636e26

File tree

10 files changed

+140
-102
lines changed

10 files changed

+140
-102
lines changed

drjava/src/edu/rice/cs/drjava/DrJava.java

Lines changed: 12 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -116,12 +116,12 @@ public class DrJava {
116116
/** Starts running DrJava.
117117
* @param args Command line argument array
118118
*/
119-
public static void main(final String[] args) {
120-
// Platform-specific UI setup.
121-
PlatformFactory.ONLY.beforeUISetup();
122-
119+
public static void main(final String[] args) {
123120
// handleCommandLineArgs will return true if DrJava should be loaded
124121
if (handleCommandLineArgs(args)) {
122+
// Platform-specific UI setup.
123+
PlatformFactory.ONLY.beforeUISetup();
124+
125125
if (!_forceNewInstance &&
126126
DrJava.getConfig().getSetting(edu.rice.cs.drjava.config.OptionConstants.REMOTE_CONTROL_ENABLED) &&
127127
(_filesToOpen.size()>0)) {
@@ -297,16 +297,14 @@ else if (arg.equals("-help") || arg.equals("-?")) {
297297

298298
/** Displays a usage message about the available options. */
299299
static void displayUsage() {
300-
final StringBuilder buf = new StringBuilder();
301-
buf.append("Usage: java -jar drjava.jar [OPTIONS] [FILES]\n\n");
302-
buf.append("where options include:\n");
303-
buf.append(" -config [FILE] use a custom config file\n");
304-
buf.append(" -new force the creation of a new DrJava instance;");
305-
buf.append(" do not connect to existing instance");
306-
buf.append(" -help | -? print this help message\n");
307-
buf.append(" -X<jvmOption> specify a JVM configuration option for the master DrJava JVM\n");
308-
buf.append(" -D<name>[=<value>] set a Java property for the master DrJava JVM\n");
309-
System.out.print(buf.toString());
300+
System.out.println("Usage: java -jar drjava.jar [OPTIONS] [FILES]\n");
301+
System.out.println("where options include:");
302+
System.out.println(" -config [FILE] use a custom config file");
303+
System.out.println(" -new force the creation of a new DrJava instance;");
304+
System.out.println(" do not connect to existing instance");
305+
System.out.println(" -help | -? print this help message");
306+
System.out.println(" -X<jvmOption> specify a JVM configuration option for the master DrJava JVM");
307+
System.out.println(" -D<name>[=<value>] set a Java property for the master DrJava JVM");
310308
}
311309

312310
// /** Prompts the user that the location of tools.jar needs to be specified to be able to use the compiler and/or the

drjava/src/edu/rice/cs/drjava/model/AbstractGlobalModel.java

Lines changed: 29 additions & 55 deletions
Original file line numberDiff line numberDiff line change
@@ -1324,7 +1324,7 @@ public void saveAllFiles(FileSaveSelector com) throws IOException {
13241324

13251325
/** Called by saveAllFiles in DefaultGlobalModel */
13261326
protected void saveAllFilesHelper(FileSaveSelector com) throws IOException {
1327-
1327+
boolean first = true;
13281328
boolean isProjActive = isProjectActive();
13291329

13301330
List<OpenDefinitionsDocument> docsToWrite = getOpenDefinitionsDocuments();
@@ -1334,13 +1334,14 @@ protected void saveAllFilesHelper(FileSaveSelector com) throws IOException {
13341334
// do not force Untitled document to be saved if projectActive() or unmodified
13351335
if (doc.isUntitled() && (isProjActive || ! doc.isModifiedSinceSave())) continue;
13361336
try {
1337-
if (doc.getFile().canWrite()) {
1337+
final File docFile = doc.getFile();
1338+
if (!docFile.exists() || docFile.canWrite()) {
13381339
// file is writable, save
13391340
aboutToSaveFromSaveAll(doc);
13401341
doc.saveFile(com);
13411342
}
1342-
else {
1343-
// file is read-only, ask user about it
1343+
else if (first) {
1344+
// file is read-only, ask user about it once
13441345
readOnlyDocs.add(doc);
13451346
}
13461347
}
@@ -1360,62 +1361,17 @@ protected void saveAllFilesHelper(FileSaveSelector com) throws IOException {
13601361
}
13611362
catch(FileMovedException fme) { /* ignore, don't know what to do here */ }
13621363
}
1363-
File[] res = _notifier.filesReadOnly(com, files.toArray(new File[files.size()]));
1364+
File[] res = _notifier.filesReadOnly(files.toArray(new File[files.size()]));
13641365
HashSet<File> rewriteFiles = new HashSet<File>(java.util.Arrays.asList(res));
13651366
for(OpenDefinitionsDocument odd: readOnlyDocs) {
1366-
try {
1367-
File roFile = odd.getFile();
1368-
if (rewriteFiles.contains(roFile)) {
1369-
docsToWrite.add(odd);
1370-
// try to make the file writable
1371-
// strangely enough, there is a File.setReadOnly() method, but
1372-
// no built-in way to make the file writable
1373-
// Sun recommends deleting the read-only file (does that work?)
1374-
File backup = new File(roFile.getAbsolutePath()+"~");
1375-
boolean noBackup = true;
1376-
if (backup.exists()) {
1377-
try {
1378-
noBackup = backup.delete();
1379-
}
1380-
catch(SecurityException se) {
1381-
noBackup = false;
1382-
}
1383-
}
1384-
if (noBackup) {
1385-
try {
1386-
noBackup = roFile.renameTo(backup);
1387-
roFile.createNewFile();
1388-
}
1389-
catch(SecurityException se) {
1390-
noBackup = false;
1391-
}
1392-
catch(IOException ioe) { }
1393-
try {
1394-
roFile.createNewFile();
1395-
}
1396-
catch(SecurityException se) { }
1397-
catch(IOException ioe) { }
1398-
}
1399-
if (!noBackup) {
1400-
try {
1401-
roFile.delete();
1402-
}
1403-
catch(SecurityException se) { /* can't do anything about it */ }
1404-
}
1405-
try {
1406-
edu.rice.cs.plt.io.IOUtil.copyFile(backup, roFile);
1407-
}
1408-
catch(SecurityException se) {
1409-
/* can't do anything about it */
1410-
}
1411-
catch(IOException ioe) {
1412-
/* can't do anything about it */
1413-
}
1414-
}
1367+
File roFile = odd.getFile();
1368+
if (rewriteFiles.contains(roFile)) {
1369+
docsToWrite.add(odd);
1370+
FileOps.makeWritable(roFile);
14151371
}
1416-
catch(FileMovedException fme) { /* ignore, don't know what to do here */ }
14171372
}
14181373
}
1374+
first = false;
14191375
}
14201376
}
14211377

@@ -1552,6 +1508,15 @@ else if (doc.isAuxiliaryFile()) {
15521508
* @param file where to save the project
15531509
*/
15541510
public void saveProject(File file, Hashtable<OpenDefinitionsDocument, DocumentInfoGetter> info) throws IOException {
1511+
// if file is read-only, ask if it should be made writable
1512+
if (file.exists() && !file.canWrite()) {
1513+
File[] res = _notifier.filesReadOnly(new File[] {file});
1514+
for(File roFile: res) {
1515+
FileOps.makeWritable(roFile);
1516+
}
1517+
if (res.length==0) { return; /* read-only, do not overwrite */ }
1518+
}
1519+
15551520
ProjectProfile builder = _makeProjectProfile(file, info);
15561521
// write to disk
15571522
builder.write();
@@ -3402,6 +3367,15 @@ public boolean saveFileAs(FileSaveSelector com) throws IOException {
34023367
// is one, then the file cannot be used in the Interactions Pane
34033368
if (file.getAbsolutePath().indexOf("#") != -1) _notifier.filePathContainsPound();
34043369

3370+
// if file is read-only, ask if it should be made writable
3371+
if(file.exists() && !file.canWrite()) {
3372+
File[] res = _notifier.filesReadOnly(new File[] {file});
3373+
for(File roFile: res) {
3374+
FileOps.makeWritable(roFile);
3375+
}
3376+
if (res.length==0) { return false; /* read-only, do not overwrite */ }
3377+
}
3378+
34053379
// have FileOps save the file
34063380
// System.err.println("Calling FileOps.saveFile to save it");
34073381
FileOps.saveFile(new FileOps.DefaultFileSaver(file) {

drjava/src/edu/rice/cs/drjava/model/DummyGlobalModelListener.java

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -59,10 +59,9 @@ public void handleAlreadyOpenDocument(OpenDefinitionsDocument doc) { }
5959
public void filesNotFound(File... f) { }
6060

6161
/** Called when trying to write one or more files that are read-only.
62-
* @param comFile file save selector
6362
* @param f files that are read-only
6463
* @return the files that should be attempted to be rewritten */
65-
public File[] filesReadOnly(FileSaveSelector com, File... f) { return new File[0]; }
64+
public File[] filesReadOnly(File... f) { return f; }
6665

6766
/** Called when the project's build directory has changed. */
6867
public void projectBuildDirChanged() { }

drjava/src/edu/rice/cs/drjava/model/GlobalEventNotifier.java

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -82,13 +82,14 @@ public void filesNotFound(File... f) {
8282
finally { _lock.endRead(); }
8383
}
8484

85-
public File[] filesReadOnly(FileSaveSelector com, File... f) {
85+
/** @return the intersection of all the return values from the listeners. */
86+
public File[] filesReadOnly(File... f) {
8687
_lock.startRead();
8788
java.util.LinkedList<File> files = new java.util.LinkedList<File>();
8889
for(File fi: f) { files.add(fi); }
8990
try {
9091
for (GlobalModelListener l : _listeners) {
91-
java.util.List<File> retry = java.util.Arrays.asList(l.filesReadOnly(com, f));
92+
java.util.List<File> retry = java.util.Arrays.asList(l.filesReadOnly(f));
9293
files.retainAll(retry);
9394
}
9495
}

drjava/src/edu/rice/cs/drjava/model/GlobalModelJUnitTest.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -555,6 +555,7 @@ public void testJUnitAllWithNoValidTests() throws Exception {
555555
_log.log("Second listener added to model");
556556
OpenDefinitionsDocument doc = setupDocument(NON_TESTCASE_TEXT);
557557
File file = new File(_tempDir, "NonTestCase.java");
558+
System.out.println("-----> file = "+file+" -- canWrite() = "+file.canWrite()+" -- exists() = "+file.exists());
558559
doc.saveFile(new FileSelector(file));
559560

560561
listener2.compile(doc);

drjava/src/edu/rice/cs/drjava/model/GlobalModelListener.java

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -67,10 +67,9 @@ public interface GlobalModelListener extends InteractionsListener, JavadocListen
6767
public void filesNotFound(File... f);
6868

6969
/** Called when trying to write one or more files that are read-only.
70-
* @param comFile file save selector
7170
* @param f files that are read-only
7271
* @return the files that should be attempted to be rewritten */
73-
public File[] filesReadOnly(FileSaveSelector com, File... f);
72+
public File[] filesReadOnly(File... f);
7473

7574
/** Called after a new document is created. */
7675
public void newFileCreated(OpenDefinitionsDocument doc);

drjava/src/edu/rice/cs/drjava/model/GlobalModelTestCase.java

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -734,8 +734,7 @@ public void handleAlreadyOpenDocument(OpenDefinitionsDocument doc) {
734734

735735
public void newFileCreated(OpenDefinitionsDocument doc) { listenerFail("newFileCreated fired unexpectedly"); }
736736
public void filesNotFound(File... f) { listenerFail("fileNotFound fired unexpectedly"); }
737-
public File[] filesReadOnly(FileSaveSelector com, File... f) { listenerFail("filesReadOnly fired unexpectedly");
738-
return new File[0]; }
737+
public File[] filesReadOnly(File... f) { listenerFail("filesReadOnly fired unexpectedly"); return f; }
739738
// Recent revision defers opening files until the document for a file is requested forcing the following comment out
740739
public void fileOpened(OpenDefinitionsDocument doc) { /* listenerFail("fileOpened fired unexpectedly"); */ }
741740
public void fileClosed(OpenDefinitionsDocument doc) { listenerFail("fileClosed fired unexpectedly"); }

drjava/src/edu/rice/cs/drjava/project/ProjectProfile.java

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -228,7 +228,6 @@ public void write() throws IOException {
228228
write(new FileOutputStream(_projectFile));
229229
}
230230

231-
// CHELSEA AND PATRICK WHOOOOOHOOOO!!!!!one
232231
public void write(OutputStream os) throws IOException {
233232
XMLConfig xc = new XMLConfig();
234233
xc.set("drjava.version", edu.rice.cs.drjava.Version.getBuildTimeString()+"-"+edu.rice.cs.drjava.Version.getRevisionNumber());

drjava/src/edu/rice/cs/drjava/ui/MainFrame.java

Lines changed: 30 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -4408,7 +4408,6 @@ private void _close() {
44084408
}
44094409
else {
44104410
fileName = l.size()+" files";
4411-
//CHELSEA AND PATRICK WHOOOOOOOOOOO!!!!
44124411
String text = "Closing these "+fileName+" will permanently remove them from the current project." +
44134412
"\nAre you sure that you want to close these files?";
44144413

@@ -7986,7 +7985,7 @@ public void filesNotFound(File... files) {
79867985
}
79877986
}
79887987

7989-
public File[] filesReadOnly(FileSaveSelector com, File... files) {
7988+
public File[] filesReadOnly(File... files) {
79907989
if (files.length == 0) return new File[0];
79917990
_fnfCount += files.length;
79927991

@@ -8216,32 +8215,39 @@ public void run() {
82168215
}
82178216

82188217
public void interactionEnded() {
8219-
InteractionsModel im = _model.getInteractionsModel();
8220-
String lastError = im.getLastError();
8221-
if (DrJava.getConfig().getSetting(edu.rice.cs.drjava.config.OptionConstants.DIALOG_AUTOIMPORT_ENABLED)) {
8222-
if (lastError != null) {
8223-
// the interaction ended and there was an error
8224-
// check that this error is different than the last one (second to last may be null):
8225-
if (!lastError.equals(im.getSecondToLastError())) {
8226-
// this aborts the auto-importing if the same class comes up twice in a row
8227-
if (lastError.startsWith("Static Error: Undefined class '") && lastError.endsWith("'")) {
8228-
// it was an "undefined class" exception
8229-
// show auto-import dialog
8230-
String undefinedClassName = lastError.substring(lastError.indexOf('\'')+1,
8231-
lastError.lastIndexOf('\''));
8232-
_showAutoImportDialog(undefinedClassName);
8233-
}
8234-
else if (lastError.startsWith("java.lang.OutOfMemoryError")) {
8235-
askToIncreaseSlaveMaxHeap();
8218+
final InteractionsModel im = _model.getInteractionsModel();
8219+
// a strange NullPointerException was reported in this method ([ 1938268 ])
8220+
// just making sure that all references are non-null
8221+
if (im != null) {
8222+
final String lastError = im.getLastError();
8223+
final edu.rice.cs.drjava.config.FileConfiguration config = DrJava.getConfig();
8224+
if ((config != null) &&
8225+
(config.getSetting(edu.rice.cs.drjava.config.OptionConstants.DIALOG_AUTOIMPORT_ENABLED))) {
8226+
if (lastError != null) {
8227+
// the interaction ended and there was an error
8228+
// check that this error is different than the last one (second to last may be null):
8229+
final String secondToLastError = im.getSecondToLastError();
8230+
if ((secondToLastError!=null) &&
8231+
(!lastError.equals(secondToLastError))) {
8232+
// this aborts the auto-importing if the same class comes up twice in a row
8233+
if (lastError.startsWith("Static Error: Undefined class '") && lastError.endsWith("'")) {
8234+
// it was an "undefined class" exception
8235+
// show auto-import dialog
8236+
String undefinedClassName = lastError.substring(lastError.indexOf('\'')+1,
8237+
lastError.lastIndexOf('\''));
8238+
_showAutoImportDialog(undefinedClassName);
8239+
}
8240+
else if (lastError.startsWith("java.lang.OutOfMemoryError")) {
8241+
askToIncreaseSlaveMaxHeap();
8242+
}
82368243
}
82378244
}
82388245
}
8246+
else {
8247+
// reset the last errors, so the dialog works again if it is re-enabled
8248+
im.resetLastErrors();
8249+
}
82398250
}
8240-
else {
8241-
// reset the last errors, so the dialog works again if it is re-enabled
8242-
im.resetLastErrors();
8243-
}
8244-
82458251
Utilities.invokeLater(new Runnable() {
82468252
public void run() {
82478253
_enableInteractionsPane();

drjava/src/edu/rice/cs/util/FileOps.java

Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -882,4 +882,66 @@ public static File getValidDirectory(final File origFile) {
882882
* to work.)
883883
*/
884884
public static URL toURL(File f) throws MalformedURLException { return f.toURI().toURL(); }
885+
886+
public static boolean makeWritable(File roFile) throws IOException {
887+
// try to make the file writable
888+
// strangely enough, there is a File.setReadOnly() method, but
889+
// no built-in way to make the file writable
890+
// Sun recommends deleting the read-only file (does that work on all operating systems?)
891+
boolean shouldBackup = edu.rice.cs.drjava.DrJava.getConfig().
892+
getSetting(edu.rice.cs.drjava.config.OptionConstants.BACKUP_FILES);
893+
boolean madeBackup = false;
894+
File backup = new File(roFile.getAbsolutePath()+"~");
895+
try {
896+
boolean noBackup = true;
897+
if (backup.exists()) {
898+
try {
899+
noBackup = backup.delete();
900+
}
901+
catch(SecurityException se) {
902+
noBackup = false;
903+
}
904+
}
905+
if (noBackup) {
906+
try {
907+
noBackup = roFile.renameTo(backup);
908+
madeBackup = true;
909+
roFile.createNewFile();
910+
}
911+
catch(SecurityException se) {
912+
noBackup = false;
913+
}
914+
catch(IOException ioe) { }
915+
try {
916+
roFile.createNewFile();
917+
}
918+
catch(SecurityException se) { }
919+
catch(IOException ioe) { }
920+
}
921+
if (!noBackup) {
922+
try {
923+
roFile.delete();
924+
}
925+
catch(SecurityException se) { return false; }
926+
}
927+
try {
928+
edu.rice.cs.plt.io.IOUtil.copyFile(backup, roFile);
929+
}
930+
catch(SecurityException se) {
931+
return false;
932+
}
933+
catch(IOException ioe) {
934+
return false;
935+
}
936+
return true;
937+
}
938+
finally {
939+
if (!shouldBackup && madeBackup) {
940+
try {
941+
backup.delete();
942+
}
943+
catch(Exception e) { /* not so important if we made a backup and now can't delete it */ }
944+
}
945+
}
946+
}
885947
}

0 commit comments

Comments
 (0)