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

Last change on this file since 206 was 206, checked in by bjn8, 16 years ago

Reverted back... but left latest changes in comments with TODO tags.
Chart widget package broken at the moment but expeditee runs fine.
Widgets will be revised ASAP

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