1 33 34 package edu.rice.cs.drjava.project; 35 36 import java.io.File ; 37 import java.io.IOException ; 38 import java.io.FileNotFoundException ; 39 import java.util.List ; 40 import java.util.ArrayList ; 41 import java.util.Date ; 42 import java.text.SimpleDateFormat ; 43 44 import edu.rice.cs.drjava.config.FileOption; 45 import edu.rice.cs.plt.tuple.Pair; 46 import edu.rice.cs.util.sexp.*; 47 import edu.rice.cs.drjava.model.DocumentRegion; 48 import edu.rice.cs.drjava.model.SimpleDocumentRegion; 49 import edu.rice.cs.drjava.model.OpenDefinitionsDocument; 50 import edu.rice.cs.drjava.model.debug.DebugWatchData; 51 import edu.rice.cs.drjava.model.debug.DebugBreakpointData; 52 import edu.rice.cs.drjava.model.debug.DebugException; 53 54 68 public class ProjectFileParser { 69 70 public static final ProjectFileParser ONLY = new ProjectFileParser(); 71 72 private File _projectFile; 73 private String _parent; 74 private String _srcFileBase; 75 76 BreakpointListVisitor breakpointListVisitor = new BreakpointListVisitor(); 77 BookmarkListVisitor bookmarkListVisitor = new BookmarkListVisitor(); 78 79 private ProjectFileParser() { } 80 81 82 83 86 public ProjectFileIR parse(File projFile) throws IOException , FileNotFoundException , MalformedProjectFileException { 87 88 _projectFile = projFile; 89 _parent = projFile.getParent(); 90 _srcFileBase = _parent; 93 List <SEList> forest = null; 94 try { forest = SExpParser.parse(projFile); } 95 catch(SExpParseException e) { throw new MalformedProjectFileException("Parse Error: " + e.getMessage()); } 96 97 ProjectFileIR pfir = new ProjectProfile(projFile); 98 99 try { for (SEList exp : forest) evaluateExpression(exp, pfir, new DocFileListVisitor(_parent)); } 100 catch(PrivateProjectException e) { throw new MalformedProjectFileException("Parse Error: " + e.getMessage()); } 101 102 104 return pfir; 105 } 106 107 112 private void evaluateExpression(SEList e, ProjectFileIR pfir, DocFileListVisitor flv) throws IOException { 113 if (e == Empty.ONLY) return; 114 Cons exp = (Cons) e; 116 String name = exp.accept(NameVisitor.ONLY); 117 if ((name.compareToIgnoreCase("source") == 0) || (name.compareToIgnoreCase("source-files") == 0)) { 118 List <DocFile> dfList = exp.getRest().accept(new DocFileListVisitor(_srcFileBase)); 119 pfir.setSourceFiles(dfList); 120 } 121 else if (name.compareToIgnoreCase("proj-root") == 0) { List <DocFile> fList = exp.getRest().accept(flv); 123 if (fList.size() > 1) throw new PrivateProjectException("Cannot have multiple source roots"); 124 else if (fList.size() == 0) pfir.setProjectRoot(null); pfir.setProjectRoot(fList.get(0)); 126 } 127 else if (name.compareToIgnoreCase("proj-root-and-base") == 0) { List <DocFile> fList = exp.getRest().accept(flv); 129 if (fList.size() > 1) throw new PrivateProjectException("Cannot have multiple source roots"); 130 File root = fList.get(0); 131 if (! root.exists()) throw new IOException ("Project root " + root + " no longer exists"); 132 pfir.setProjectRoot(root); 133 _srcFileBase = root.getCanonicalPath(); 134 } 135 else if (name.compareToIgnoreCase("auxiliary") == 0) { 136 List <DocFile> dfList = exp.getRest().accept(flv); 137 pfir.setAuxiliaryFiles(dfList); 138 } 139 else if (name.compareToIgnoreCase("collapsed") == 0) { 140 List <String > sList = exp.getRest().accept(PathListVisitor.ONLY); 141 pfir.setCollapsedPaths(sList); 142 } 143 else if (name.compareToIgnoreCase("build-dir") == 0) { 144 List <DocFile> fList = exp.getRest().accept(flv); 145 if (fList.size() > 1) throw new PrivateProjectException("Cannot have multiple build directories"); 147 else if (fList.size() == 0) pfir.setBuildDirectory(null); 148 else pfir.setBuildDirectory(fList.get(0)); 149 } 150 else if (name.compareToIgnoreCase("work-dir") == 0) { 151 List <DocFile> fList = exp.getRest().accept(flv); 152 if (fList.size() > 1) throw new PrivateProjectException("Cannot have multiple working directories"); 153 else if (fList.size() == 0) pfir.setWorkingDirectory(null); 154 else pfir.setWorkingDirectory(fList.get(0)); 155 } 156 else if (name.compareToIgnoreCase("classpaths") == 0) { 157 List <DocFile> fList = exp.getRest().accept(flv); 158 pfir.setClassPaths(fList); 159 } 160 else if (name.compareToIgnoreCase("main-class") == 0) { 161 List <DocFile> fList = exp.getRest().accept(flv); 162 if (fList.size() > 1) throw new PrivateProjectException("Cannot have multiple main classes"); 163 else if (fList.size() == 0) pfir.setMainClass(null); 164 else pfir.setMainClass(fList.get(0)); 165 } 166 else if (name.compareToIgnoreCase("breakpoints") == 0) { 177 List <DebugBreakpointData> bpList = exp.getRest().accept(breakpointListVisitor); 178 pfir.setBreakpoints(bpList); 179 } 180 else if (name.compareToIgnoreCase("watches") == 0) { 181 List <DebugWatchData> sList = exp.getRest().accept(WatchListVisitor.ONLY); 182 pfir.setWatches(sList); 183 } 184 else if (name.compareToIgnoreCase("bookmarks") == 0) { 185 List <DocumentRegion> bmList = exp.getRest().accept(bookmarkListVisitor); 186 pfir.setBookmarks(bmList); 187 } 188 } 189 190 194 DocFile parseFile(SExp s, String pathRoot) { 195 String name = s.accept(NameVisitor.ONLY); 196 if (name.compareToIgnoreCase("file") != 0) 197 throw new PrivateProjectException("Expected a file tag, found: " + name); 198 if (! (s instanceof Cons)) 199 throw new PrivateProjectException("Expected a labeled node, found a label: " + name); 200 SEList c = ((Cons)s).getRest(); 202 DocFilePropertyVisitor v = new DocFilePropertyVisitor(pathRoot); 203 return c.accept(v); 204 } 205 206 private String parseFileName(SExp s) { 207 if (s instanceof Cons) { 208 SEList l = ((Cons)s).getRest(); 209 if (l == Empty.ONLY) 210 throw new PrivateProjectException("expected filename, but nothing found"); 211 else { 212 String name = l.accept(NameVisitor.ONLY); 213 name = edu.rice.cs.util.StringOps.replace(name,"\\","/"); 214 return name; 215 } 216 } 217 else throw new PrivateProjectException("expected name tag, found string"); 218 } 219 220 private int parseInt(SExp s) { 221 if (s instanceof Cons) { 222 SEList l = ((Cons)s).getRest(); 223 if (l == Empty.ONLY) 224 throw new PrivateProjectException("expected integer, but nothing found"); 225 else { 226 int i = l.accept(NumberVisitor.ONLY); 227 return i; 228 } 229 } 230 else throw new PrivateProjectException("expected name tag, found string"); 231 } 232 233 private Pair<Integer ,Integer > parseIntPair(SExp s) { 234 int row; 235 int col; 236 237 238 if (!(s instanceof Cons)) { 239 throw new PrivateProjectException("expected name tag, found string"); 240 } 241 242 final List <Integer > intList = new ArrayList <Integer >(); 244 SEList l = ((Cons)s).getRest(); 245 List <Integer > li = l.accept(new SExpVisitor<List <Integer >>() { 246 public List <Integer > forEmpty(Empty e) { return intList; } 247 248 public List <Integer > forCons(Cons c) { 249 c.getFirst().accept(this); 250 return c.getRest().accept(this); 251 } 252 253 public List <Integer > forBoolAtom(BoolAtom b) { 254 throw new PrivateProjectException("unexpected boolean found, int expected"); 255 } 256 257 public List <Integer > forNumberAtom(NumberAtom n) { 258 intList.add(new Integer (n.intValue())); 259 return intList; 260 } 261 262 public List <Integer > forTextAtom(TextAtom t) { 263 throw new PrivateProjectException("unexpected string found where number expected: " + t.getText()); 264 } 265 266 }); 267 268 if (li.size() == 2) return new Pair<Integer ,Integer >(li.get(0), li.get(1)); 269 else throw new PrivateProjectException("expected a list of 2 ints for select, found list of size " + li.size()); 270 } 271 272 273 private String parseStringNode(SExp n) { 274 if (n instanceof Cons) 275 return ((Cons)n).getRest().accept(NameVisitor.ONLY); 276 else throw new PrivateProjectException("List expected, but found text instead"); 277 } 278 279 280 281 282 private static class DocFileListVisitor implements SEListVisitor<List <DocFile>> { 283 284 private String _base; 285 DocFileListVisitor(String base) { _base = base; } 286 public List <DocFile> forEmpty(Empty e) { return new ArrayList <DocFile>(); } 287 public List <DocFile> forCons(Cons c) { 288 List <DocFile> list = c.getRest().accept(this); 289 DocFile tmp = ProjectFileParser.ONLY.parseFile(c.getFirst(), _base); 290 list.add(0, tmp); return list; 292 } 293 }; 294 295 296 private static class DocFilePropertyVisitor implements SEListVisitor<DocFile> { 297 private String fname = ""; 298 private Pair<Integer ,Integer > select = new Pair<Integer ,Integer >(new Integer (0),new Integer (0)); 299 private Pair<Integer ,Integer > scroll = new Pair<Integer ,Integer >(new Integer (0),new Integer (0)); 300 private boolean active = false; 301 private String pack = ""; 302 private Date modDate = null; 303 304 private String pathRoot; 305 public DocFilePropertyVisitor(String pr) { pathRoot = pr; } 306 307 public DocFile forCons(Cons c) { 308 String name = c.getFirst().accept(NameVisitor.ONLY); 309 if (name.compareToIgnoreCase("name") == 0) { fname = ProjectFileParser.ONLY.parseFileName(c.getFirst()); } 310 else if (name.compareToIgnoreCase("select") == 0) { select = ProjectFileParser.ONLY.parseIntPair(c.getFirst()); } 311 else if (name.compareToIgnoreCase("scroll") == 0) { scroll = ProjectFileParser.ONLY.parseIntPair(c.getFirst()); } 312 else if (name.compareToIgnoreCase("active") == 0) { active = true; } 313 else if (name.compareToIgnoreCase("package") == 0) { 314 pack = ProjectFileParser.ONLY.parseStringNode(c.getFirst()); 315 } 316 else if (name.compareToIgnoreCase("mod-date") == 0) { 317 String tmp = ProjectFileParser.ONLY.parseStringNode(c.getFirst()); 318 try { modDate = new SimpleDateFormat ("dd-MMM-yyyy HH:mm:ss").parse(tmp); } 319 catch (java.text.ParseException e) { throw new PrivateProjectException("Bad mod-date: " + e.getMessage()); } 320 } 321 322 return c.getRest().accept(this); 323 } 324 325 public DocFile forEmpty(Empty c) { 326 if (pathRoot == null || new File (fname).isAbsolute()) { 327 return new DocFile(fname, select, scroll, active, pack); 328 } 329 else { 330 DocFile f = new DocFile(pathRoot, fname, select, scroll, active, pack); 331 if (modDate != null) f.setSavedModDate(modDate.getTime()); 332 return f; 333 } 334 } 335 } 336 337 338 private static class PathListVisitor implements SEListVisitor<List <String >> { 339 public static final PathListVisitor ONLY = new PathListVisitor(); 340 private PathListVisitor() { } 341 342 public List <String > forEmpty(Empty e) { return new ArrayList <String >(); } 343 public List <String > forCons(Cons c) { 344 List <String > list = c.getRest().accept(this); 345 SExp first = c.getFirst(); 346 String name = first.accept(NameVisitor.ONLY); 347 if (name.compareToIgnoreCase("path") == 0) { 348 String tmp = ProjectFileParser.ONLY.parseStringNode(c.getFirst()); 349 list.add(0,tmp); } 351 return list; 352 } 353 }; 354 355 358 private static class NameVisitor implements SExpVisitor<String > { 359 public static final NameVisitor ONLY = new NameVisitor(); 360 private NameVisitor() { } 361 362 public String forEmpty(Empty e) { 363 throw new PrivateProjectException("Found an empty node, expected a labeled node"); 364 } 365 public String forCons(Cons c) { return c.getFirst().accept(this); } 366 public String forBoolAtom(BoolAtom b) { 367 throw new PrivateProjectException("Found a boolean, expected a label"); 368 } 369 public String forNumberAtom(NumberAtom n) { 370 throw new PrivateProjectException("Found a number, expected a label"); 371 } 372 public String forTextAtom(TextAtom t) { return t.getText(); } 373 }; 374 375 378 private static class NumberVisitor implements SExpVisitor<Integer > { 379 public static final NumberVisitor ONLY = new NumberVisitor(); 380 private NumberVisitor() { } 381 382 public Integer forEmpty(Empty e) { 383 throw new PrivateProjectException("Found an empty node, expected an integer"); 384 } 385 public Integer forCons(Cons c) { return c.getFirst().accept(this); } 386 public Integer forBoolAtom(BoolAtom b) { 387 throw new PrivateProjectException("Found a boolean, expected an integer"); 388 } 389 public Integer forNumberAtom(NumberAtom n) { return n.intValue(); } 390 public Integer forTextAtom(TextAtom t) { 391 throw new PrivateProjectException("Found a string '"+t+"', expected an integer"); 392 } 393 }; 394 395 396 private static class WatchListVisitor implements SEListVisitor<List <DebugWatchData>> { 397 public static final WatchListVisitor ONLY = new WatchListVisitor(); 398 private WatchListVisitor() { } 399 400 public List <DebugWatchData> forEmpty(Empty e) { return new ArrayList <DebugWatchData>(); } 401 public List <DebugWatchData> forCons(Cons c) { 402 List <DebugWatchData> list = c.getRest().accept(this); 403 SExp first = c.getFirst(); 404 String name = first.accept(NameVisitor.ONLY); 405 if (name.compareToIgnoreCase("watch") == 0) { 406 String tmp = ProjectFileParser.ONLY.parseStringNode(c.getFirst()); 407 list.add(0,new DebugWatchData(tmp)); } 409 return list; 410 } 411 }; 412 413 415 416 private class BreakpointListVisitor implements SEListVisitor<List <DebugBreakpointData>> { 417 public List <DebugBreakpointData> forEmpty(Empty e) { return new ArrayList <DebugBreakpointData>(); } 418 public List <DebugBreakpointData> forCons(Cons c) { 419 List <DebugBreakpointData> list = c.getRest().accept(this); 420 DebugBreakpointData tmp = ProjectFileParser.ONLY.parseBreakpoint(c.getFirst(), _srcFileBase); 421 list.add(0, tmp); return list; 423 } 424 }; 425 426 430 DebugBreakpointData parseBreakpoint(SExp s, String pathRoot) { 431 String name = s.accept(NameVisitor.ONLY); 432 if (name.compareToIgnoreCase("breakpoint") != 0) 433 throw new PrivateProjectException("Expected a breakpoint tag, found: " + name); 434 if (! (s instanceof Cons)) 435 throw new PrivateProjectException("Expected a labeled node, found a label: " + name); 436 SEList c = ((Cons)s).getRest(); 438 BreakpointPropertyVisitor v = new BreakpointPropertyVisitor(pathRoot); 439 return c.accept(v); 440 } 441 442 443 444 private static class BreakpointPropertyVisitor implements SEListVisitor<DebugBreakpointData> { 445 private String fname = null; 446 private Integer offset = null; 447 private Integer lineNumber = null; 448 private boolean isEnabled = false; 449 450 private String pathRoot; 451 public BreakpointPropertyVisitor(String pr) { pathRoot = pr; } 452 453 public DebugBreakpointData forCons(Cons c) { 454 String name = c.getFirst().accept(NameVisitor.ONLY); 455 if (name.compareToIgnoreCase("name") == 0) { fname = ProjectFileParser.ONLY.parseFileName(c.getFirst()); } 456 else if (name.compareToIgnoreCase("offset") == 0) { offset = ProjectFileParser.ONLY.parseInt(c.getFirst()); } 457 else if (name.compareToIgnoreCase("line") == 0) { lineNumber = ProjectFileParser.ONLY.parseInt(c.getFirst()); } 458 else if (name.compareToIgnoreCase("enabled") == 0) { isEnabled = true; } 459 460 return c.getRest().accept(this); 461 } 462 463 public DebugBreakpointData forEmpty(Empty c) { 464 if ((fname == null) || (offset == null) || (lineNumber == null)) { 465 throw new PrivateProjectException("Breakpoint information incomplete, need name, offset and line tags"); 466 } 467 if (pathRoot == null || new File (fname).isAbsolute()) { 468 final File f = new File (fname); 469 return new DebugBreakpointData() { 470 public File getFile() { return f; } 471 public int getOffset() { return offset; } 472 public int getLineNumber() { return lineNumber; } 473 public boolean isEnabled() { return isEnabled; } 474 }; 475 } 476 else { 477 final File f = new File (pathRoot, fname); 478 return new DebugBreakpointData() { 479 public File getFile() { return f; } 480 public int getOffset() { return offset; } 481 public int getLineNumber() { return lineNumber; } 482 public boolean isEnabled() { return isEnabled; } 483 }; 484 } 485 } 486 } 487 488 490 491 private class BookmarkListVisitor implements SEListVisitor<List <DocumentRegion>> { 492 public List <DocumentRegion> forEmpty(Empty e) { return new ArrayList <DocumentRegion>(); } 493 public List <DocumentRegion> forCons(Cons c) { 494 List <DocumentRegion> list = c.getRest().accept(this); 495 DocumentRegion tmp = ProjectFileParser.ONLY.parseBookmark(c.getFirst(), _srcFileBase); 496 list.add(0, tmp); return list; 498 } 499 }; 500 501 505 DocumentRegion parseBookmark(SExp s, String pathRoot) { 506 String name = s.accept(NameVisitor.ONLY); 507 if (name.compareToIgnoreCase("bookmark") != 0) 508 throw new PrivateProjectException("Expected a bookmark tag, found: " + name); 509 if (! (s instanceof Cons)) 510 throw new PrivateProjectException("Expected a labeled node, found a label: " + name); 511 SEList c = ((Cons)s).getRest(); 513 BookmarkPropertyVisitor v = new BookmarkPropertyVisitor(pathRoot); 514 return c.accept(v); 515 } 516 517 518 520 private static class BookmarkPropertyVisitor implements SEListVisitor<DocumentRegion> { 521 private String fname = null; 522 private Integer startOffset = null; 523 private Integer endOffset = null; 524 525 private String pathRoot; 526 public BookmarkPropertyVisitor(String pr) { pathRoot = pr; } 527 528 public DocumentRegion forCons(Cons c) { 529 String name = c.getFirst().accept(NameVisitor.ONLY); 530 if (name.compareToIgnoreCase("name") == 0) { fname = ProjectFileParser.ONLY.parseFileName(c.getFirst()); } 531 else if (name.compareToIgnoreCase("start") == 0) { startOffset = ProjectFileParser.ONLY.parseInt(c.getFirst()); } 532 else if (name.compareToIgnoreCase("end") == 0) { endOffset = ProjectFileParser.ONLY.parseInt(c.getFirst()); } 533 534 return c.getRest().accept(this); 535 } 536 537 public DocumentRegion forEmpty(Empty c) { 538 if ((fname == null) || (startOffset == null) || (endOffset == null)) { 539 throw new PrivateProjectException("Bookmark information incomplete, need name, start offset and end offset"); 540 } 541 File f; 542 if (pathRoot == null || new File (fname).isAbsolute()) { 543 f = new File (fname); 544 } 545 else { 546 f = new File (pathRoot, fname); 547 } 548 return new SimpleDocumentRegion(null, f, startOffset, endOffset); 549 } 550 } 551 552 private static class PrivateProjectException extends RuntimeException { 553 public PrivateProjectException(String message) { super(message); } 554 } 555 } 556 | Popular Tags |