source: trunk/src/org/expeditee/gui/FrameIO.java@ 104

Last change on this file since 104 was 104, checked in by ra33, 16 years ago

Made it so stats will update on cached version of the frame when backing away from a frame

File size: 33.5 KB
Line 
1package org.expeditee.gui;
2
3import java.io.BufferedReader;
4import java.io.BufferedWriter;
5import java.io.File;
6import java.io.FileInputStream;
7import java.io.FileOutputStream;
8import java.io.FileReader;
9import java.io.FileWriter;
10import java.io.IOException;
11import java.util.HashMap;
12
13import org.expeditee.agents.ExistingFramesetException;
14import org.expeditee.io.Conversion;
15import org.expeditee.io.ExpReader;
16import org.expeditee.io.ExpWriter;
17import org.expeditee.io.FrameReader;
18import org.expeditee.io.FrameWriter;
19import org.expeditee.io.KMSReader;
20import org.expeditee.io.KMSWriter;
21import org.expeditee.io.Logger;
22import org.expeditee.items.Item;
23import org.expeditee.items.ItemUtils;
24import org.expeditee.items.Text;
25import org.expeditee.stats.SessionStats;
26
27/**
28 * This class provides static methods for all saving and loading of Frames
29 * to\from disk. This class also handles any caching of previously loaded
30 * Frames.
31 *
32 * @author jdm18
33 *
34 */
35public class FrameIO {
36
37 private static final char FRAME_NAME_LAST_CHAR = 'A';
38
39 public static void changeParentFolder(String newFolder) {
40 PARENT_FOLDER = newFolder;
41 FRAME_PATH = PARENT_FOLDER + "framesets" + File.separator;
42 TRASH_PATH = PARENT_FOLDER + "trash" + File.separator;
43 IMAGES_PATH = PARENT_FOLDER + IMAGES_FOLDER;
44 HELP_PATH = PARENT_FOLDER + "documentation" + File.separator;
45 PROFILE_PATH = PARENT_FOLDER + "profiles" + File.separator;
46 EXPORTS_DIR = PARENT_FOLDER + "exports" + File.separator;
47 STATISTICS_DIR = PARENT_FOLDER + "statistics" + File.separator;
48 LOGS_DIR = PARENT_FOLDER + "logs" + File.separator;
49 }
50
51 /**
52 * The default location for storing the framesets. Each frameset has its own
53 * subdirectory in this directory.
54 */
55 public static String IMAGES_FOLDER = "images" + File.separator;
56
57 public static String TRASH_PATH;
58
59 public static String PARENT_FOLDER;
60
61 public static String FRAME_PATH;
62
63 public static String IMAGES_PATH;
64
65 public static String HELP_PATH;
66
67 public static String PROFILE_PATH;
68
69 public static String EXPORTS_DIR;
70
71 public static String STATISTICS_DIR;
72
73 public static String LOGS_DIR;
74
75 private static final String INF_FILENAME = "frame.inf";
76
77 public static final String ILLEGAL_CHARS = ";:\\/?";
78
79 public static final int MAX_NAME_LENGTH = 64;
80
81 public static final int MAX_CACHE = 100;
82
83 private static HashMap<String, Frame> _Cache = new HashMap<String, Frame>();
84
85 // private static HashMap<String, String> _FramesetNameCache = new
86 // HashMap<String, String>();
87
88 private static boolean ENABLE_CACHE = true;
89
90 private static boolean _UseCache = true;
91
92 private static boolean _SuspendedCache = false;
93
94 // All methods are static, this should not be instantiated
95 private FrameIO() {
96 }
97
98 public static boolean isCacheOn() {
99 return _UseCache && ENABLE_CACHE;
100 }
101
102 public static void Precache(String framename) {
103 // if the cache is turned off, do nothing
104 if (!isCacheOn())
105 return;
106
107 // if the frame is already in the cache, do nothing
108 if (_Cache.containsKey(framename.toLowerCase()))
109 return;
110
111 // otherwise, load the frame and put it in the cache
112 Logger.Log(Logger.SYSTEM, Logger.LOAD, "Precaching " + framename + ".");
113
114 // do not display errors encountered to the user
115 // (they will be shown at load time)
116 FrameGraphics.SupressMessages(true);
117 // loading automatically caches the frame is caching is turned on
118 LoadUnknownPath(framename);
119 FrameGraphics.SupressMessages(false);
120 }
121
122 /**
123 * Checks if a string is a representation of a positive integer.
124 *
125 * @param s
126 * @return true if s is a positive integer
127 */
128 public static boolean isPositiveInteger(String s) {
129 if (s == null || s.length() == 0)
130 return false;
131
132 for (int i = 0; i < s.length(); i++) {
133 if (!Character.isDigit(s.charAt(i)))
134 return false;
135 }
136 return true;
137 }
138
139 public static Frame LoadFrame(String frameName) {
140 if (!isValidFrameName(frameName))
141 return null;
142
143 // first try reading from cache
144 if (isCacheOn() && _Cache.containsKey(frameName.toLowerCase())) {
145 Logger.Log(Logger.SYSTEM, Logger.LOAD, "Loading " + frameName
146 + " from cache.");
147 return _Cache.get(frameName.toLowerCase());
148 }
149
150 Logger.Log(Logger.SYSTEM, Logger.LOAD, "Loading " + frameName
151 + " from disk.");
152 return LoadUnknownPath(frameName);
153 }
154
155 private static Frame LoadUnknownPath(String framename) {
156 Frame loaded = null;
157
158 for (String path : UserSettings.FrameDirs) {
159 loaded = LoadFrame(path, framename);
160 if (loaded != null) {
161 FrameUtils.Parse(loaded, true);
162 break;
163 }
164 }
165
166 return loaded;
167 }
168
169 /**
170 * Gets a list of all the framesets available to the user
171 *
172 * @return a string containing a list of all the available framesets on
173 * separate lines
174 */
175 public static String getFramesetList() {
176 StringBuffer list = new StringBuffer();
177
178 for (String path : UserSettings.FrameDirs) {
179 File files = new File(path);
180 if (!files.exists())
181 continue;
182 for (File f : (new File(path)).listFiles()) {
183 if (f.isDirectory()) {
184 list.append(f.getName()).append('\n');
185 }
186 }
187 }
188 // remove the final new line char
189 list.deleteCharAt(list.length() - 1);
190 return list.toString();
191 }
192
193 /**
194 * Gets the full path and file name of the frame.
195 *
196 * @param path-
197 * the directory in which to look for the frameset containing the
198 * frame.
199 * @param frameName-
200 * the name of the frame for which the path is being requested.
201 * @return null if the frame can not be located.
202 */
203 public static String getFrameFullPathName(String path, String frameName) {
204 String source = path + Conversion.getFramesetName(frameName)
205 + File.separator;
206
207 File tester = new File(source);
208 if (!tester.exists())
209 return null;
210
211 // check for the new file name format
212 String fullPath = source + Conversion.getFrameNumber(frameName)
213 + ExpReader.EXTENTION;
214 tester = new File(fullPath);
215
216 if (tester.exists())
217 return fullPath;
218
219 // check for oldfile name format
220 fullPath = source + Conversion.getFramesetName(frameName) + "."
221 + Conversion.getFrameNumber(frameName);
222 tester = new File(fullPath);
223
224 if (tester.exists())
225 return fullPath;
226
227 return null;
228 }
229
230 public static boolean canAccessFrame(String frameName) {
231 for (String path : UserSettings.FrameDirs) {
232 if (getFrameFullPathName(path, frameName) != null)
233 return true;
234 }
235 return false;
236 }
237
238 private static Frame LoadFrame(String path, String frameName) {
239 String fullPath = getFrameFullPathName(path, frameName);
240 if (fullPath == null)
241 return null;
242
243 try {
244 FrameReader reader;
245
246 if (fullPath.endsWith(ExpReader.EXTENTION)) {
247 reader = new ExpReader(frameName);
248 } else {
249 reader = new KMSReader();
250 }
251 Frame frame = reader.readFrame(fullPath);
252
253 if (frame == null) {
254 FrameGraphics.ErrorMessage("Error: " + frameName
255 + " could not be successfully loaded.");
256 return null;
257 }
258
259 frame.path = path;
260
261 // do not put 0 frames or virtual frames into the cache
262 if (_Cache.size() > MAX_CACHE)
263 _Cache.clear();
264
265 if (frame.getNumber() > 0 && isCacheOn())
266 _Cache.put(frameName.toLowerCase(), frame);
267
268 return frame;
269 } catch (IOException ioe) {
270 ioe.printStackTrace();
271 Logger.Log(ioe);
272 } catch (Exception e) {
273 e.printStackTrace();
274 Logger.Log(e);
275 FrameGraphics.ErrorMessage("Error: " + frameName
276 + " could not be successfully loaded.");
277 }
278
279 return null;
280 }
281
282 public static void Reload() {
283 // disable cache
284 boolean cache = _UseCache;
285
286 _UseCache = false;
287 Frame fresh = FrameIO.LoadFrame(DisplayIO.getCurrentFrame().getName());
288 _UseCache = cache;
289 if (_Cache.containsKey(fresh.getName().toLowerCase()))
290 _Cache.put(fresh.getName().toLowerCase(), fresh);
291 DisplayIO.setCurrentFrame(fresh);
292 }
293
294 public static Frame LoadPrevious(Frame current) {
295 checkTDFC(current);
296
297 // the current name and number
298 String name = current.getFramesetName();
299 int num = current.getNumber() - 1;
300
301 // loop until a frame that exists is found
302 for (; num >= 0; num--) {
303 Frame f = LoadFrame(name + num);
304 if (f != null)
305 return f;
306 }
307
308 // if we did not find another Frame then this one must be the last one
309 // in the frameset
310 FrameGraphics
311 .DisplayMessageOnce("This is the first frame in the frameset");
312 return null;
313 }
314
315 /**
316 * Returns the next Frame in the current Frameset (The Frame with the next
317 * highest Frame number) If the current Frame is the last one in the
318 * Frameset, or an error occurs then null is returned.
319 *
320 * @return The Frame after this one in the current frameset, or null
321 */
322 public static Frame LoadNext(Frame current) {
323 checkTDFC(current);
324
325 // the current name and number
326 int num = current.getNumber() + 1;
327 int max = num + 1;
328 String name = current.getFramesetName();
329
330 // read the maximum from the INF file
331 try {
332 max = ReadINF(current.path, current.getFramesetName());
333 } catch (IOException ioe) {
334 FrameGraphics.ErrorMessage("Error loading INF file for frameset '"
335 + name + "'");
336 return null;
337 }
338
339 // loop until a frame that exists is found
340 for (; num <= max; num++) {
341 Frame f = LoadFrame(name + num);
342 if (f != null)
343 return f;
344 }
345
346 // if we did not find another Frame then this one must be the last one
347 // in the frameset
348 FrameGraphics
349 .DisplayMessageOnce("This is the last frame in the frameset");
350 return null;
351 }
352
353 /**
354 * This method checks if the current frame has just been created with TDFC.
355 * If it has the frame is saved regardless of whether it has been edited or
356 * not and the TDFC item property is cleared. This is to ensure that the
357 * link is saved on the parent frame.
358 *
359 * @param current
360 */
361 public static void checkTDFC(Frame current) {
362 if (FrameUtils.getTdfcItem() != null) {
363 FrameUtils.setTdfcItem(null);
364 current.change();
365 }
366 }
367
368 public static Frame LoadLast(String framesetName, String path) {
369 // read the maximum from the INF file
370 int max;
371 try {
372 max = ReadINF(path, framesetName);
373 } catch (IOException ioe) {
374 FrameGraphics.ErrorMessage("Error loading INF file for frameset '"
375 + framesetName + "'");
376 return null;
377 }
378
379 // loop backwards until a frame that exists is found
380 for (int num = max; num > 0; num--) {
381 Frame f = LoadFrame(framesetName + num);
382 if (f != null)
383 return f;
384 }
385
386 // if we did not find another Frame then this one must be the last one
387 // in the frameset
388 FrameGraphics.DisplayMessage("This is the last frame in the frameset");
389 return null;
390 }
391
392 public static Frame LoadZero(String framesetName, String path) {
393 return LoadFrame(framesetName + 0);
394 }
395
396 public static Frame LoadZero() {
397 Frame current = DisplayIO.getCurrentFrame();
398 return LoadZero(current.getFramesetName(), current.path);
399 }
400
401 public static Frame LoadLast() {
402 Frame current = DisplayIO.getCurrentFrame();
403 return LoadLast(current.getFramesetName(), current.path);
404 }
405
406 public static Frame LoadNext() {
407 return LoadNext(DisplayIO.getCurrentFrame());
408 }
409
410 public static Frame LoadPrevious() {
411 return LoadPrevious(DisplayIO.getCurrentFrame());
412 }
413
414 /**
415 * Deletes the given Frame on disk and removes the cached Frame if there is
416 * one. Also adds the deleted frame into the deletedFrames frameset.
417 *
418 * @param toDelete
419 * The Frame to be deleted
420 * @return The result of File.delete()
421 */
422 public static boolean DeleteFrame(Frame toDelete) throws IOException,
423 SecurityException {
424 if (toDelete == null)
425 return false;
426
427 // Dont delete the zero frame
428 if (toDelete.getNumber() == 0) {
429 throw new SecurityException("Deleting a zero frame is illegal");
430 }
431
432 SaveFrame(toDelete);
433
434 // Copy deleted frames to the DeletedFrames frameset
435 // get the last used frame in the destination frameset
436 final String DELETED_FRAMES = "DeletedFrames";
437 int lastNumber = FrameIO.getLastNumber(DELETED_FRAMES);
438 String framePath;
439 try {
440 // create the new frameset
441 Frame one = FrameIO.CreateFrameset(DELETED_FRAMES, toDelete.path);
442 framePath = one.path;
443 lastNumber = 0;
444 } catch (Exception e) {
445 Frame zero = FrameIO.LoadFrame(DELETED_FRAMES + "0");
446 framePath = zero.path;
447 }
448
449 // get the fill path to determine which file version it is
450 String source = getFrameFullPathName(toDelete.path, toDelete.getName());
451
452 String oldFrameName = toDelete.getName().toLowerCase();
453 // Now save the frame in the new location
454 toDelete.setFrameset(DELETED_FRAMES);
455 toDelete.setFrameNumber(lastNumber + 1);
456 toDelete.path = framePath;
457 FrameIO.SaveFrame(toDelete);
458
459 if (_Cache.containsKey(oldFrameName))
460 _Cache.remove(oldFrameName);
461
462 File del = new File(source);
463
464 java.io.FileInputStream ff = new java.io.FileInputStream(del);
465 ff.close();
466
467 return del.delete();
468 }
469
470 /**
471 * Creates a new Frame in the given frameset and assigns it the given Title,
472 * which can be null. The newly created Frame is a copy of the frameset's .0
473 * file with the number updated based on the last recorded Frame name in the
474 * frameset's INF file.
475 *
476 * @param frameset
477 * The frameset to create the new Frame in
478 * @param frameTitle
479 * The title to assign to the newly created Frame (can be NULL).
480 * @return The newly created Frame.
481 */
482 public static Frame CreateFrame(String frameset, String frameTitle,
483 String templateFrame) throws RuntimeException {
484
485 if (!FrameIO.isValidFramesetName(frameset)) {
486 throw new RuntimeException(frameset
487 + " is not a valid frameset name");
488 }
489
490 int next = -1;
491
492 // disable caching of 0 frames
493 SuspendCache();
494 String zeroFrameName = frameset + "0";
495 Frame destFramesetZero = LoadFrame(zeroFrameName);
496 if (destFramesetZero == null) {
497 throw new RuntimeException(zeroFrameName + " could not be found");
498 }
499
500 Frame template = null;
501 if (templateFrame == null) {
502 // load in frame.0
503 template = destFramesetZero;
504 } else {
505 template = LoadFrame(templateFrame);
506 if (template == null) {
507 throw new RuntimeException("LinkTemplate " + templateFrame
508 + " could not be found");
509 }
510 }
511
512 ResumeCache();
513
514 // read the next number from the INF file
515 try {
516 next = ReadINF(destFramesetZero.path, frameset);
517 } catch (IOException ioe) {
518 ioe.printStackTrace();
519 throw new RuntimeException("INF file could not be read");
520 }
521
522 // Remove the old frame from the cashe then add the new one
523 // TODO figure out some way that we can put both in the cache
524 _Cache.remove(template.getName().toLowerCase());
525 // set the number and title of the new frame
526 template.setName(frameset, ++next);
527 template.setTitle(frameTitle);
528 // _Cache.put(template.getName().toLowerCase(), template);
529
530 Logger.Log(Logger.SYSTEM, Logger.TDFC, "Creating new frame: "
531 + template.getName() + " from TDFC");
532
533 // update INF file
534 try {
535 WriteINF(template.path, frameset, frameset + next);
536 } catch (IOException ioe) {
537 ioe.printStackTrace();
538 Logger.Log(ioe);
539 }
540
541 template.setOwner(UserSettings.Username);
542 template.resetDateCreated();
543 for (Item i : template.getItems()) {
544 if (ItemUtils.startsWithTag(i, ItemUtils.TAG_PARENT))
545 i.setLink(null);
546 }
547
548 return template;
549 }
550
551 public static void DisableCache() {
552 _UseCache = false;
553 }
554
555 public static void EnableCache() {
556 _UseCache = true;
557 }
558
559 public static void SuspendCache() {
560 if (_UseCache) {
561 DisableCache();
562 _SuspendedCache = true;
563 } else {
564 _SuspendedCache = false;
565 }
566 }
567
568 public static void ResumeCache() {
569 if (_SuspendedCache) {
570 EnableCache();
571 _SuspendedCache = false;
572 }
573 }
574
575 public static void RefreshCasheImages() {
576 SuspendCache();
577 for (Frame f : _Cache.values())
578 f.setBuffer(null);
579 ResumeCache();
580 }
581
582 /**
583 * Creates a new frameset using the given name. This includes creating a new
584 * subdirectory in the <code>FRAME_PATH</code> directory, Copying over the
585 * default.0 frame from the default frameset, copying the .0 Frame to make a
586 * .1 Frame, and creating the frameset's INF file.
587 *
588 * @param frameset
589 * The name of the Frameset to create
590 * @return The first Frame of the new Frameset (Frame.1)
591 */
592 public static Frame CreateFrameset(String frameset, String path)
593 throws Exception {
594 return CreateFrameset(frameset, path, false);
595 }
596
597 /**
598 * Tests if the given String is a 'proper' framename, that is, the String
599 * must begin with a character, end with a number with 0 or more letters and
600 * numbers in between.
601 *
602 * @param frameName
603 * The String to test for validity as a frame name
604 * @return True if the given framename is proper, false otherwise.
605 */
606 public static boolean isValidFrameName(String frameName) {
607
608 if (frameName == null || frameName.length() < 2)
609 return false;
610
611 int lastCharIndex = frameName.length() - 1;
612 // String must begin with a letter and end with a digit
613 if (!Character.isLetter(frameName.charAt(0))
614 || !Character.isDigit(frameName.charAt(lastCharIndex)))
615 return false;
616
617 // All the characters between first and last must be letters
618 // or digits
619 for (int i = 1; i < lastCharIndex; i++) {
620 if (!Character.isLetterOrDigit(frameName.charAt(i)))
621 return false;
622 }
623 return true;
624 }
625
626 /**
627 * Saves the given Frame to disk in the corresponding frameset directory.
628 * This is the same as calling SaveFrame(toSave, true)
629 *
630 * @param toSave
631 * The Frame to save to disk in KMS format.
632 */
633 public static String SaveFrame(Frame toSave) {
634 return SaveFrame(toSave, true);
635 }
636
637 public static String SaveFrame(Frame toSave, boolean inc) {
638 return SaveFrame(toSave, inc, true);
639 }
640
641 /**
642 * Saves the given Frame to disk in the corresponding frameset directory, if
643 * inc is true then the saved frames counter is incremented, otherwise it is
644 * untouched.
645 *
646 * @param toSave
647 * The Frame to save to disk in KMS format.
648 * @param inc
649 * True if the saved frames counter should be incremented, false
650 * otherwise.
651 * @param checkBackup
652 * True if the frame should be checked for the back up tag
653 */
654 public static String SaveFrame(Frame toSave, boolean inc,
655 boolean checkBackup) {
656
657 if (toSave == null)
658 return "";
659
660 if (!toSave.hasChanged() || toSave.isSaved())
661 return "";
662
663 // Get the full path only to determine which format to use for saving
664 // the frame
665 // At this stage use Exp format for saving Exp frames only.
666 // Later this will be changed so that KMS frames will be updated to the
667 // Exp format.
668 String fullPath = getFrameFullPathName(toSave.path, toSave.getName());
669
670 // Dont save if the frame is protected and it exists
671 if (checkBackup
672 && toSave.getNameItem().Permission < Item.PERMISSION_TDFC) {
673 _Cache.remove(toSave.getName().toLowerCase());
674 return "";
675 }
676
677 // Check if the frame exists
678 if (checkBackup && fullPath == null) {
679 // The first time a frame with the backup tag is saved, dont back it
680 // up
681 checkBackup = false;
682 }
683
684 FrameWriter writer = null;
685 int savedVersion;
686 try {
687 // if its a new frame or an existing Exp frame...
688 if (fullPath == null || fullPath.endsWith(ExpReader.EXTENTION)) {
689 writer = new ExpWriter();
690 savedVersion = ExpReader.getVersion(fullPath);
691 } else {
692 writer = new KMSWriter();
693 savedVersion = KMSReader.getVersion(fullPath);
694 }
695 // Check if we are trying to save an out of date version
696 if (savedVersion > toSave.getVersion(false)
697 && !toSave.getFramesetName().equalsIgnoreCase(
698 FrameGraphics.MESSAGES_FRAMESET_NAME)) {
699 // remove this frame from the cache if it is there
700 // This will make sure links to the original are set correctly
701 _Cache.remove(toSave.getName().toLowerCase());
702 int nextnum = ReadINF(toSave.path, toSave.getFramesetName()) + 1;
703 SuspendCache();
704 Frame original = LoadFrame(toSave.getName());
705 toSave.setFrameNumber(nextnum);
706 ResumeCache();
707 // Put the modified version in the cache
708 _Cache.put(toSave.getName().toLowerCase(), toSave);
709 // Show the messages alerting the user
710 Text originalMessage = new Text(-1);
711 originalMessage.setColor(FrameGraphics.ERROR_COLOR);
712 originalMessage.setText(original.getName()
713 + " was updated by another user.");
714 originalMessage.setLink(original.getName());
715 Text yourMessage = new Text(-1);
716 yourMessage.setColor(FrameGraphics.ERROR_COLOR);
717 yourMessage.setText("Your version was renamed "
718 + toSave.getName());
719 yourMessage.setLink(toSave.getName());
720 FrameGraphics.DisplayMessage(originalMessage);
721 FrameGraphics.DisplayMessage(yourMessage);
722 } else if (checkBackup
723 && ItemUtils.ContainsExactTag(toSave.getItems(),
724 ItemUtils.TAG_BACKUP)) {
725 SuspendCache();
726 Frame original = LoadFrame(toSave.getName());
727 if (original == null)
728 original = toSave;
729 int orignum = original.getNumber();
730 int nextnum = ReadINF(toSave.path, toSave.getFramesetName()) + 1;
731
732 original.setFrameNumber(nextnum);
733 original.setProtection("" + Item.PERMISSION_COPY);
734 original.change();
735 SaveFrame(original, false, false);
736
737 Item i = ItemUtils.FindExactTag(toSave.getItems(),
738 ItemUtils.TAG_BACKUP);
739 i.setLink(original.getName());
740 toSave.setFrameNumber(orignum);
741 ResumeCache();
742 }
743 toSave.setActiveTime(SessionStats.getFrameActiveTime());
744 toSave.setDarkTime(SessionStats.getFrameDarkTime());
745 toSave.setLastModifyDate(Logger.EasyDateFormat("ddMMMyyyy:HHmm"));
746 toSave.setLastModifyUser(UserSettings.Username);
747 writer.writeFrame(toSave);
748 toSave.setSaved();
749 if (inc)
750 SessionStats.SavedFrame(toSave.getName());
751
752 // avoid out-of-sync frames (when in TwinFrames mode)
753 if (_Cache.containsKey(toSave.getName().toLowerCase()))
754 _Cache.put(toSave.getName().toLowerCase(), toSave);
755
756 Logger.Log(Logger.SYSTEM, Logger.SAVE, "Saving " + toSave.getName()
757 + " to disk.");
758
759 // check that the INF file is not out of date
760 int last = ReadINF(toSave.path, toSave.getFramesetName());
761 if (last <= toSave.getNumber())
762 WriteINF(toSave.path, toSave.getFramesetName(), toSave
763 .getName());
764
765 // check if this was the profile frame (and thus needs
766 // re-parsing)
767 if (isProfileFrame(toSave)) {
768 Frame profile = FrameIO.LoadFrame(toSave.getFramesetName()
769 + "1");
770 assert (profile != null);
771 FrameUtils.ParseProfile(profile);
772 }
773 } catch (IOException ioe) {
774 ioe.getStackTrace();
775 Logger.Log(ioe);
776 return null;
777 }
778
779 return writer.getFileContents();
780 }
781
782 /**
783 * Checks if a frame is in the current user profile frameset.
784 *
785 * @param toCheck
786 * the frame to check
787 * @return true if the frame is in the current user profile frameset
788 */
789 public static boolean isProfileFrame(Frame toCheck) {
790 if (toCheck.getNumber() == 0)
791 return false;
792 return toCheck.getFramesetName()
793 .equalsIgnoreCase(UserSettings.Username);
794 }
795
796 public static Frame LoadProfile(String userName) {
797 return LoadFrame(userName + "1");
798 }
799
800 public static Frame CreateNewProfile(String username) throws Exception {
801 Frame profile = CreateFrameset(username, PROFILE_PATH, true);
802 FrameUtils.CreateDefaultProfile(profile);
803 return profile;
804 }
805
806 /**
807 * Reads the INF file that corresponds to the given Frame name
808 *
809 * @param framename
810 * The Frame to lookup the INF file for
811 * @throws IOException
812 * Any exceptions encountered by the BufferedReader used to read
813 * the INF.
814 */
815 private static int ReadINF(String path, String frameset) throws IOException {
816 assert (!frameset.endsWith("."));
817
818 // read INF
819 BufferedReader reader;
820 try {
821 reader = new BufferedReader(new FileReader(path
822 + frameset.toLowerCase() + File.separator + INF_FILENAME));
823 } catch (Exception e) {
824 reader = new BufferedReader(new FileReader(path
825 + frameset.toLowerCase() + File.separator
826 + frameset.toLowerCase() + ".inf"));
827 }
828 String inf = reader.readLine();
829 reader.close();
830
831 return Conversion.getFrameNumber(inf);
832 }
833
834 /**
835 * Writes the given String out to the INF file corresponding to the current
836 * frameset.
837 *
838 * @param toWrite
839 * The String to write to the file.
840 * @throws IOException
841 * Any exception encountered by the BufferedWriter.
842 */
843 private static void WriteINF(String path, String frameset, String frameName)
844 throws IOException {
845 assert (!frameset.endsWith("."));
846
847 path += frameset.toLowerCase() + File.separator + INF_FILENAME;
848
849 BufferedWriter writer = new BufferedWriter(new FileWriter(path));
850 writer.write(frameName);
851 writer.close();
852 }
853
854 public static boolean FrameIsCached(String name) {
855 return _Cache.containsKey(name);
856 }
857
858 public static String ConvertToValidFramesetName(String toValidate) {
859 assert (toValidate != null && toValidate.length() > 0);
860
861 StringBuffer result = new StringBuffer();
862
863 if (Character.isDigit(toValidate.charAt(0))) {
864 result.append(FRAME_NAME_LAST_CHAR);
865 }
866
867 boolean capital = false;
868 for (int i = 0; i < toValidate.length()
869 && result.length() < MAX_NAME_LENGTH; i++) {
870 char cur = toValidate.charAt(i);
871
872 // capitalize all characters after spaces
873 if (Character.isLetterOrDigit(cur)) {
874 if (capital) {
875 capital = false;
876 result.append(Character.toUpperCase(cur));
877 } else
878 result.append(cur);
879 } else {
880 capital = true;
881 }
882 }
883 assert (result.length() > 0);
884 int lastCharIndex = result.length() - 1;
885 if (!Character.isLetter(result.charAt(lastCharIndex))) {
886 if (lastCharIndex == MAX_NAME_LENGTH - 1)
887 result.setCharAt(lastCharIndex, FRAME_NAME_LAST_CHAR);
888 else
889 result.append(FRAME_NAME_LAST_CHAR);
890 }
891
892 assert (isValidFramesetName(result.toString()));
893 return result.toString();
894 }
895
896 public static Frame CreateNewFrame(Item linker) throws RuntimeException {
897 String title = linker.getName();
898
899 String templateLink = linker.getAbsoluteLinkTemplate();
900 String framesetLink = linker.getAbsoluteLinkFrameset();
901 String frameset = (framesetLink != null ? framesetLink : DisplayIO
902 .getCurrentFrame().getFramesetName());
903
904 Frame newFrame = FrameIO.CreateFrame(frameset, title, templateLink);
905
906 // do auto shrinking of the title IF not in twin frames mode
907 Item titleItem = newFrame.getTitleItem();
908
909 if (!DisplayIO.isTwinFramesOn()) {
910 // BROOK: This had recursion!! Changed to avoid...
911 if ((titleItem.getX() + 1) < newFrame.getNameItem().getX()) {
912 while (titleItem.getBoundsWidth() + titleItem.getX() > newFrame
913 .getNameItem().getX()) {
914 titleItem.setSize(titleItem.getSize() - 1);
915 }
916 } else {
917 System.out.println("Bad title x position: " + titleItem.getX());
918 }
919 }
920
921 return newFrame;
922 }
923
924 /**
925 * Creates a new Frameset on disk, including a .0, .1, and .inf files. The
926 * Default.0 frame is copied to make the initial .0 and .1 Frames
927 *
928 * @param name
929 * The Frameset name to use
930 * @return The name of the first Frame in the newly created Frameset (the .1
931 * frame)
932 */
933 public static Frame CreateNewFrameset(String name) throws Exception {
934 String path = DisplayIO.getCurrentFrame().path;
935
936 // if current frameset is profile directory change it to framesets
937 if (path.equals(FrameIO.PROFILE_PATH)) {
938 path = FrameIO.FRAME_PATH;
939 }
940
941 return FrameIO.CreateFrameset(name, path);
942 }
943
944 /**
945 *
946 * @param frameset
947 * @return
948 */
949 public static int getLastNumber(String frameset) { // Rob thinks it might
950 // have been
951 // GetHighestNumExFrame
952 // TODO minimise the number of frames being read in!!
953 int num = -1;
954
955 Frame zero = LoadFrame(frameset + "0");
956
957 // the frameset does not exist (or has no 0 frame)
958 if (zero == null)
959 return -1;
960
961 try {
962 num = ReadINF(zero.path, frameset);
963 } catch (IOException e) {
964 // TODO Auto-generated catch block
965 // e.printStackTrace();
966 }
967
968 /*
969 * Michael doesnt think the code below is really needed... it will just
970 * slow things down when we are reading frames over a network***** for (;
971 * num >= 0; num--) { System.out.println("This code is loading frames to
972 * find the highest existing frame..."); if (LoadFrame(frameset + num) !=
973 * null) break; }
974 */
975
976 return num;
977 }
978
979 /**
980 * Checks if a given frameset is accessable.
981 *
982 * @param framesetName
983 * @return
984 */
985 public static Boolean canAccessFrameset(String framesetName) {
986 framesetName = framesetName.toLowerCase();
987 for (String path : UserSettings.FrameDirs) {
988 if ((new File(path + framesetName)).exists())
989 return true;
990 }
991 return false;
992 }
993
994 public static Frame CreateFrameset(String frameset, String path,
995 boolean recreate) throws Exception {
996 String conversion = frameset + " --> ";
997
998 if (!isValidFramesetName(frameset)) {
999 throw new Exception("Invalid frameset name");
1000 }
1001
1002 if (!recreate && FrameIO.canAccessFrameset(frameset)) {
1003 throw new ExistingFramesetException(frameset);
1004 }
1005
1006 conversion += frameset;
1007 Logger.Log(Logger.SYSTEM, Logger.NEW_FRAMESET, "Frameset Name: "
1008 + conversion);
1009 conversion = frameset;
1010
1011 /**
1012 * TODO: Update this to exclude any\all invalid filename characters
1013 */
1014 // ignore annotation character
1015 if (frameset.startsWith("@"))
1016 frameset = frameset.substring(1);
1017
1018 conversion += " --> " + frameset;
1019 Logger.Log(Logger.SYSTEM, Logger.NEW_FRAMESET, "Name: " + conversion);
1020
1021 // create the new Frameset directory
1022 File dir = new File(path + frameset.toLowerCase() + File.separator);
1023
1024 dir.mkdirs();
1025
1026 // create the new INF file
1027 try {
1028 WriteINF(path, frameset, frameset + '1');
1029 } catch (IOException ioe) {
1030 ioe.printStackTrace();
1031 Logger.Log(ioe);
1032 }
1033
1034 SuspendCache();
1035 // copy the default .0 and .1 files
1036 Frame base = null;
1037 try {
1038 base = LoadFrame(UserSettings.DefaultFrame);
1039 } catch (Exception e) {
1040 }
1041 // The frame may not be accessed for various reasons... in all these
1042 // cases just create a new one
1043 if (base == null) {
1044 base = new Frame();
1045 }
1046
1047 ResumeCache();
1048
1049 base.resetDateCreated();
1050 base.setFrameset(frameset);
1051 base.setFrameNumber(0);
1052 base.setTitle(base.getFramesetName() + "0");
1053 base.path = path;
1054 base.change();
1055 SaveFrame(base, false);
1056
1057 base.resetDateCreated();
1058 base.setFrameNumber(1);
1059 base.setTitle(frameset);
1060 base.change();
1061 SaveFrame(base, true);
1062
1063 Logger.Log(Logger.SYSTEM, Logger.NEW_FRAMESET, "Created new frameset: "
1064 + frameset);
1065
1066 return base;
1067 }
1068
1069 /**
1070 * Tests if a frameset name is valid. That is it must begin and end with a
1071 * letter and contain only letters and digits in between.
1072 *
1073 * @param frameset
1074 * the name to be tested
1075 * @return true if the frameset name is valid
1076 */
1077 public static boolean isValidFramesetName(String frameset) {
1078 if (frameset == null) {
1079 return false;
1080 }
1081
1082 int nameLength = frameset.length();
1083 if (frameset.length() <= 0 || nameLength > MAX_NAME_LENGTH) {
1084 return false;
1085 }
1086
1087 int lastCharIndex = nameLength - 1;
1088
1089 if (!Character.isLetter(frameset.charAt(0))
1090 || !Character.isLetter(frameset.charAt(lastCharIndex)))
1091 return false;
1092
1093 for (int i = 1; i < lastCharIndex; i++) {
1094 if (!Character.isLetterOrDigit(frameset.charAt(i))) {
1095 return false;
1096 }
1097 }
1098 return true;
1099 }
1100
1101 public static boolean DeleteFrameset(String framesetName) {
1102 if (!FrameIO.canAccessFrameset(framesetName))
1103 return false;
1104 // Search all the available directories for the directory
1105 for (String path : UserSettings.FrameDirs) {
1106 String source = path + framesetName.toLowerCase() + File.separator;
1107 File framesetDirectory = new File(source);
1108 // Once we have found the directory move it to the trash
1109 if (framesetDirectory.exists()) {
1110 String destPath = FrameIO.TRASH_PATH
1111 + framesetName.toLowerCase();
1112 int copyNumber = 1;
1113 File dest = new File(destPath + File.separator);
1114 // Create the trash folder if it doesnt already exist
1115 if (!dest.getParentFile().exists())
1116 dest.mkdirs();
1117 // If a frameset with the same name is already in the trash add
1118 // a number to the end
1119 while (dest.exists()) {
1120 dest = new File(destPath + ++copyNumber + File.separator);
1121 }
1122 if (!framesetDirectory.renameTo(dest)) {
1123 for (File f : framesetDirectory.listFiles()) {
1124 if (!f.delete())
1125 return false;
1126 }
1127 if (!framesetDirectory.delete())
1128 return false;
1129 }
1130 return true;
1131 }
1132 }
1133 return false;
1134 }
1135
1136 public static boolean CopyFrameset(String framesetToCopy,
1137 String copiedFrameset) throws Exception {
1138 if (!FrameIO.canAccessFrameset(framesetToCopy))
1139 return false;
1140 if (FrameIO.canAccessFrameset(copiedFrameset))
1141 return false;
1142 // search through all the directories to find the frameset we are
1143 // copying
1144 for (String path : UserSettings.FrameDirs) {
1145 String source = path + framesetToCopy.toLowerCase()
1146 + File.separator;
1147 File framesetDirectory = new File(source);
1148 if (framesetDirectory.exists()) {
1149 // copy the frameset
1150 File copyFramesetDirectory = new File(path
1151 + copiedFrameset.toLowerCase() + File.separator);
1152 if (!copyFramesetDirectory.mkdirs())
1153 return false;
1154 // copy each of the frames
1155 for (File f : framesetDirectory.listFiles()) {
1156 // Ignore hidden files
1157 if (f.getName().charAt(0) == '.')
1158 continue;
1159 String copyPath = copyFramesetDirectory.getAbsolutePath()
1160 + File.separator + f.getName();
1161 FrameIO.copyFile(f.getAbsolutePath(), copyPath);
1162 }
1163 return true;
1164 }
1165 }
1166 return false;
1167 }
1168
1169 /**
1170 * Copies a file from one location to another.
1171 *
1172 * @param existingFile
1173 * @param newFileName
1174 * @throws Exception
1175 */
1176 public static void copyFile(String existingFile, String newFileName)
1177 throws Exception {
1178 FileInputStream is = new FileInputStream(existingFile);
1179 FileOutputStream os = new FileOutputStream(newFileName, false);
1180 int data;
1181 while ((data = is.read()) != -1) {
1182 os.write(data);
1183 }
1184 os.flush();
1185 os.close();
1186 is.close();
1187 }
1188
1189 /**
1190 * Saves a frame regardless of whether or not the frame is marked as having
1191 * been changed.
1192 *
1193 * @param frame
1194 * the frame to save
1195 * @return the contents of the frame or null if it could not be saved
1196 */
1197 public static String ForceSaveFrame(Frame frame) {
1198 frame.change();
1199 return SaveFrame(frame, false);
1200 }
1201
1202 public static boolean isValidLink(String frameName) {
1203 return frameName == null || isPositiveInteger(frameName)
1204 || isValidFrameName(frameName);
1205 }
1206
1207}
Note: See TracBrowser for help on using the repository browser.