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

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

Fixed a few bugs that were makin unit tests fail...
Also added Greenstone search of HCI Bib tex

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