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

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

Added @autoFormat tag

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