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

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

Added some HELP actions
More mail stuff... and networking stuff
Can now save frames and send messages to peers
Also version control prevent versions from being lost

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