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

Last change on this file since 424 was 424, checked in by ra33, 16 years ago
File size: 42.8 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.util.Collection;
18import java.util.HashMap;
19import java.util.LinkedList;
20
21import org.expeditee.actions.Actions;
22import org.expeditee.agents.ExistingFramesetException;
23import org.expeditee.io.Conversion;
24import org.expeditee.io.ExpReader;
25import org.expeditee.io.ExpWriter;
26import org.expeditee.io.FrameReader;
27import org.expeditee.io.FrameWriter;
28import org.expeditee.io.KMSReader;
29import org.expeditee.io.KMSWriter;
30import org.expeditee.items.Item;
31import org.expeditee.items.ItemUtils;
32import org.expeditee.items.Permission;
33import org.expeditee.items.Text;
34import org.expeditee.network.FrameShare;
35import org.expeditee.stats.Formatter;
36import org.expeditee.stats.Logger;
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.reset();
663 template.resetDateCreated();
664
665 for (Item i : template.getItems()) {
666 if (ItemUtils.startsWithTag(i, ItemUtils.TAG_PARENT))
667 i.setLink(null);
668 }
669
670 // do auto shrinking of the title IF not in twin frames mode
671 Item titleItem = template.getTitleItem();
672
673 if (!DisplayIO.isTwinFramesOn()) {
674 if ((titleItem.getX() + 1) < template.getNameItem().getX()) {
675 while (titleItem.getSize() > Text.MINIMUM_FONT_SIZE
676 && titleItem.getBoundsWidth() + titleItem.getX() > template
677 .getNameItem().getX()) {
678 titleItem.setSize(titleItem.getSize() - 1);
679
680 }
681 } else {
682 System.out.println("Bad title x position: " + titleItem.getX());
683 }
684 }
685
686 // Assign a width to the title.
687 titleItem.setRightMargin(template.getNameItem().getX(), true);
688
689 return template;
690 }
691
692 public static void DisableCache() {
693 _UseCache = false;
694 }
695
696 public static void EnableCache() {
697 _UseCache = true;
698 }
699
700 public static void SuspendCache() {
701 if (_UseCache) {
702 DisableCache();
703 _SuspendedCache = true;
704 } else {
705 _SuspendedCache = false;
706 }
707 }
708
709 public static void ResumeCache() {
710 if (_SuspendedCache) {
711 EnableCache();
712 _SuspendedCache = false;
713 }
714 }
715
716 public static void RefreshCasheImages() {
717 SuspendCache();
718 for (Frame f : _Cache.values())
719 f.setBuffer(null);
720 ResumeCache();
721 }
722
723 /**
724 * Creates a new frameset using the given name. This includes creating a new
725 * subdirectory in the <code>FRAME_PATH</code> directory, Copying over the
726 * default.0 frame from the default frameset, copying the .0 Frame to make a
727 * .1 Frame, and creating the frameset's INF file.
728 *
729 * @param frameset
730 * The name of the Frameset to create
731 * @return The first Frame of the new Frameset (Frame.1)
732 */
733 public static Frame CreateFrameset(String frameset, String path)
734 throws Exception {
735 return CreateFrameset(frameset, path, false);
736 }
737
738 /**
739 * Tests if the given String is a 'proper' framename, that is, the String
740 * must begin with a character, end with a number with 0 or more letters and
741 * numbers in between.
742 *
743 * @param frameName
744 * The String to test for validity as a frame name
745 * @return True if the given framename is proper, false otherwise.
746 */
747 public static boolean isValidFrameName(String frameName) {
748
749 if (frameName == null || frameName.length() < 2)
750 return false;
751
752 int lastCharIndex = frameName.length() - 1;
753 // String must begin with a letter and end with a digit
754 if (!Character.isLetter(frameName.charAt(0))
755 || !Character.isDigit(frameName.charAt(lastCharIndex)))
756 return false;
757
758 // All the characters between first and last must be letters
759 // or digits
760 for (int i = 1; i < lastCharIndex; i++) {
761 if (!Character.isLetterOrDigit(frameName.charAt(i)))
762 return false;
763 }
764 return true;
765 }
766
767 /**
768 * Saves the given Frame to disk in the corresponding frameset directory.
769 * This is the same as calling SaveFrame(toSave, true)
770 *
771 * @param toSave
772 * The Frame to save to disk
773 */
774 public static String SaveFrame(Frame toSave) {
775 return SaveFrame(toSave, true);
776 }
777
778 /**
779 * Saves a frame.
780 *
781 * @param toSave
782 * the frame to save
783 * @param inc
784 * true if the frames counter should be incremented
785 * @return the text content of the frame
786 */
787 public static String SaveFrame(Frame toSave, boolean inc) {
788 return SaveFrame(toSave, inc, true);
789 }
790
791 /**
792 * Saves the given Frame to disk in the corresponding frameset directory, if
793 * inc is true then the saved frames counter is incremented, otherwise it is
794 * untouched.
795 *
796 * @param toSave
797 * The Frame to save to disk
798 * @param inc
799 * True if the saved frames counter should be incremented, false
800 * otherwise.
801 * @param checkBackup
802 * True if the frame should be checked for the back up tag
803 */
804 public static String SaveFrame(Frame toSave, boolean inc,
805 boolean checkBackup) {
806
807 // TODO When loading a frame maybe append onto the event history too-
808 // with a
809 // break to indicate the end of a session
810
811 if (toSave == null || !toSave.hasChanged() || toSave.isSaved()) {
812 return "";
813 }
814
815 // Dont save if the frame is protected and it exists
816 if (checkBackup && toSave.isReadOnly()) {
817 _Cache.remove(toSave.getName().toLowerCase());
818 return "";
819 }
820
821 /* Dont save the frame if it has the noSave tag */
822 if (toSave.hasAnnotation("nosave")) {
823 Actions.PerformActionCatchErrors(toSave, null, "Restore");
824 return "";
825 }
826
827 // Save frame that is not local through the Networking classes
828 // TODO
829 if (!toSave.isLocal()) {
830 return FrameShare.getInstance().saveFrame(toSave);
831 }
832
833 /* Format the frame if it has the autoFormat tag */
834 if (toSave.hasAnnotation("autoformat")) {
835 Actions.PerformActionCatchErrors(toSave, null, "Format");
836 }
837
838 /**
839 * Get the full path only to determine which format to use for saving
840 * the frame. At this stage use Exp format for saving Exp frames only.
841 * Later this will be changed so that KMS frames will be updated to the
842 * Exp format.
843 */
844 String fullPath = getFrameFullPathName(toSave.getPath(), toSave
845 .getName());
846
847 // Check if the frame exists
848 if (checkBackup && fullPath == null) {
849 // The first time a frame with the backup tag is saved, dont back it
850 // up
851 checkBackup = false;
852 }
853
854 FrameWriter writer = null;
855 int savedVersion;
856 try {
857 // if its a new frame or an existing Exp frame...
858 if (fullPath == null || fullPath.endsWith(ExpReader.EXTENTION)) {
859 writer = new ExpWriter();
860 savedVersion = ExpReader.getVersion(fullPath);
861 } else {
862 writer = new KMSWriter();
863 savedVersion = KMSReader.getVersion(fullPath);
864 }
865
866 // Check if the frame doesnt exist
867 // if (savedVersion < 0) {
868 // /*
869 // * This will happen if the user has two Expeditee's running at
870 // * once and closes the first. When the second one closes the
871 // * messages directory will have been deleted.
872 // */
873 // MessageBay
874 // .errorMessage("Could not save frame that does not exist: "
875 // + toSave.getName());
876 // return null;
877 // }
878
879 // Check if we are trying to save an out of date version
880 if (savedVersion > toSave.getVersion()
881 && !toSave.getFramesetName().equalsIgnoreCase(
882 MessageBay.MESSAGES_FRAMESET_NAME)) {
883 // remove this frame from the cache if it is there
884 // This will make sure links to the original are set correctly
885 _Cache.remove(toSave.getName().toLowerCase());
886 int nextnum = ReadINF(toSave.getPath(), toSave
887 .getFramesetName(), false) + 1;
888 SuspendCache();
889 Frame original = LoadFrame(toSave.getName());
890 toSave.setFrameNumber(nextnum);
891 ResumeCache();
892 // Put the modified version in the cache
893 addToCache(toSave);
894 // Show the messages alerting the user
895 Text originalMessage = new Text(-1);
896 originalMessage.setColor(MessageBay.ERROR_COLOR);
897 originalMessage.setText(original.getName()
898 + " was updated by another user.");
899 originalMessage.setLink(original.getName());
900 Text yourMessage = new Text(-1);
901 yourMessage.setColor(MessageBay.ERROR_COLOR);
902 yourMessage.setText("Your version was renamed "
903 + toSave.getName());
904 yourMessage.setLink(toSave.getName());
905 MessageBay.displayMessage(originalMessage);
906 MessageBay.displayMessage(yourMessage);
907 } else if (checkBackup
908 && ItemUtils.ContainsExactTag(toSave.getItems(),
909 ItemUtils.TAG_BACKUP)) {
910 SuspendCache();
911 Frame original = LoadFrame(toSave.getName());
912 if (original == null)
913 original = toSave;
914 int orignum = original.getNumber();
915 int nextnum = ReadINF(toSave.getPath(), toSave
916 .getFramesetName(), false) + 1;
917
918 original.setFrameNumber(nextnum);
919 original.setPermission(Permission.copy);
920 original.change();
921 SaveFrame(original, false, false);
922
923 Item i = ItemUtils.FindExactTag(toSave.getItems(),
924 ItemUtils.TAG_BACKUP);
925 i.setLink(original.getName());
926 toSave.setFrameNumber(orignum);
927 ResumeCache();
928 }
929 // Update general stuff about frame
930 setSavedProperties(toSave);
931
932 // int oldMode = FrameGraphics.getMode();
933 // if (oldMode != FrameGraphics.MODE_XRAY)
934 // FrameGraphics.setMode(FrameGraphics.MODE_XRAY, true);
935 writer.writeFrame(toSave);
936 // FrameGraphics.setMode(oldMode, true);
937 toSave.setSaved();
938 if (inc) {
939 SessionStats.SavedFrame(toSave.getName());
940 }
941
942 // avoid out-of-sync frames (when in TwinFrames mode)
943 if (_Cache.containsKey(toSave.getName().toLowerCase()))
944 addToCache(toSave);
945
946 Logger.Log(Logger.SYSTEM, Logger.SAVE, "Saving " + toSave.getName()
947 + " to disk.");
948
949 // check that the INF file is not out of date
950 int last = ReadINF(toSave.getPath(), toSave.getFramesetName(),
951 false);
952 if (last <= toSave.getNumber())
953 WriteINF(toSave.getPath(), toSave.getFramesetName(), toSave
954 .getName());
955
956 // check if this was the profile frame (and thus needs
957 // re-parsing)
958 if (isProfileFrame(toSave)) {
959 Frame profile = FrameIO.LoadFrame(toSave.getFramesetName()
960 + "1");
961 assert (profile != null);
962 FrameUtils.ParseProfile(profile);
963 }
964 } catch (IOException ioe) {
965 ioe.printStackTrace();
966 ioe.getStackTrace();
967 Logger.Log(ioe);
968 return null;
969 }
970
971 return writer.getFileContents();
972 }
973
974 /**
975 * @param toAdd
976 */
977 public static void addToCache(Frame toAdd) {
978 _Cache.put(toAdd.getName().toLowerCase(), toAdd);
979 }
980
981 /**
982 * Checks if a frame is in the current user profile frameset.
983 *
984 * @param toCheck
985 * the frame to check
986 * @return true if the frame is in the current user profile frameset
987 */
988 public static boolean isProfileFrame(Frame toCheck) {
989 if (toCheck.getNumber() == 0)
990 return false;
991 return toCheck.getPath().equals(PROFILE_PATH);
992 // return toCheck.getFramesetName()
993 // .equalsIgnoreCase(UserSettings.ProfileName);
994 }
995
996 public static Frame LoadProfile(String userName) {
997 return LoadFrame(userName + "1");
998 }
999
1000 public static Frame CreateNewProfile(String username) throws Exception {
1001 Frame profile = CreateFrameset(username, PROFILE_PATH, true);
1002 FrameUtils.CreateDefaultProfile(username, profile);
1003 return profile;
1004 }
1005
1006 /**
1007 * Reads the INF file that corresponds to the given Frame name
1008 *
1009 * @param framename
1010 * The Frame to lookup the INF file for
1011 * @throws IOException
1012 * Any exceptions encountered by the BufferedReader used to read
1013 * the INF.
1014 */
1015 public static int ReadINF(String path, String frameset, boolean update)
1016 throws IOException {
1017 assert (!frameset.endsWith("."));
1018 try {
1019 // read INF
1020 BufferedReader reader;
1021 try {
1022 // Check on the local drive
1023 reader = new BufferedReader(new FileReader(path
1024 + frameset.toLowerCase() + File.separator
1025 + INF_FILENAME));
1026 } catch (Exception e) {
1027 reader = new BufferedReader(new FileReader(path
1028 + frameset.toLowerCase() + File.separator
1029 + frameset.toLowerCase() + ".inf"));
1030 }
1031 String inf = reader.readLine();
1032 reader.close();
1033
1034 int next = Conversion.getFrameNumber(inf);
1035 // update INF file
1036 if (update) {
1037 try {
1038 WriteINF(path, frameset, frameset + (next + 1));
1039 } catch (IOException ioe) {
1040 ioe.printStackTrace();
1041 Logger.Log(ioe);
1042 }
1043 }
1044 return next;
1045 } catch (Exception e) {
1046 }
1047
1048 // Check peers
1049 return FrameShare.getInstance().getInfNumber(path, frameset, update);
1050 }
1051
1052 /**
1053 * Writes the given String out to the INF file corresponding to the current
1054 * frameset.
1055 *
1056 * @param toWrite
1057 * The String to write to the file.
1058 * @throws IOException
1059 * Any exception encountered by the BufferedWriter.
1060 */
1061 public static void WriteINF(String path, String frameset, String frameName)
1062 throws IOException {
1063 try {
1064 assert (!frameset.endsWith("."));
1065
1066 path += frameset.toLowerCase() + File.separator + INF_FILENAME;
1067
1068 BufferedWriter writer = new BufferedWriter(new FileWriter(path));
1069 writer.write(frameName);
1070 writer.close();
1071 } catch (Exception e) {
1072
1073 }
1074 }
1075
1076 public static boolean FrameIsCached(String name) {
1077 return _Cache.containsKey(name);
1078 }
1079
1080 /**
1081 * Gets a frame from the cache.
1082 *
1083 * @param name
1084 * The frame to get from the cache
1085 *
1086 * @return The frame from cache. Null if not cached.
1087 */
1088 public static Frame FrameFromCache(String name) {
1089 return _Cache.get(name);
1090 }
1091
1092 public static String ConvertToValidFramesetName(String toValidate) {
1093 assert (toValidate != null && toValidate.length() > 0);
1094
1095 StringBuffer result = new StringBuffer();
1096
1097 if (Character.isDigit(toValidate.charAt(0))) {
1098 result.append(FRAME_NAME_LAST_CHAR);
1099 }
1100
1101 boolean capital = false;
1102 for (int i = 0; i < toValidate.length()
1103 && result.length() < MAX_NAME_LENGTH; i++) {
1104 char cur = toValidate.charAt(i);
1105
1106 // capitalize all characters after spaces
1107 if (Character.isLetterOrDigit(cur)) {
1108 if (capital) {
1109 capital = false;
1110 result.append(Character.toUpperCase(cur));
1111 } else
1112 result.append(cur);
1113 } else {
1114 capital = true;
1115 }
1116 }
1117 assert (result.length() > 0);
1118 int lastCharIndex = result.length() - 1;
1119 if (!Character.isLetter(result.charAt(lastCharIndex))) {
1120 if (lastCharIndex == MAX_NAME_LENGTH - 1)
1121 result.setCharAt(lastCharIndex, FRAME_NAME_LAST_CHAR);
1122 else
1123 result.append(FRAME_NAME_LAST_CHAR);
1124 }
1125
1126 assert (isValidFramesetName(result.toString()));
1127 return result.toString();
1128 }
1129
1130 public static Frame CreateNewFrame(Item linker) throws RuntimeException {
1131 String title = linker.getName();
1132
1133 String templateLink = linker.getAbsoluteLinkTemplate();
1134 String framesetLink = linker.getAbsoluteLinkFrameset();
1135 String frameset = (framesetLink != null ? framesetLink : DisplayIO
1136 .getCurrentFrame().getFramesetName());
1137
1138 Frame newFrame = FrameIO.CreateFrame(frameset, title, templateLink);
1139 return newFrame;
1140 }
1141
1142 /**
1143 * Creates a new Frameset on disk, including a .0, .1, and .inf files. The
1144 * Default.0 frame is copied to make the initial .0 and .1 Frames
1145 *
1146 * @param name
1147 * The Frameset name to use
1148 * @return The name of the first Frame in the newly created Frameset (the .1
1149 * frame)
1150 */
1151 public static Frame CreateNewFrameset(String name) throws Exception {
1152 String path = DisplayIO.getCurrentFrame().getPath();
1153
1154 // if current frameset is profile directory change it to framesets
1155 if (path.equals(FrameIO.PROFILE_PATH)) {
1156 path = FrameIO.FRAME_PATH;
1157 }
1158
1159 return FrameIO.CreateFrameset(name, path);
1160 }
1161
1162 /**
1163 *
1164 * @param frameset
1165 * @return
1166 */
1167 public static int getLastNumber(String frameset) { // Rob thinks it might
1168 // have been
1169 // GetHighestNumExFrame
1170 // TODO minimise the number of frames being read in!!
1171 int num = -1;
1172
1173 Frame zero = LoadFrame(frameset + "0");
1174
1175 // the frameset does not exist (or has no 0 frame)
1176 if (zero == null)
1177 return -1;
1178
1179 try {
1180 num = ReadINF(zero.getPath(), frameset, false);
1181 } catch (IOException e) {
1182 // TODO Auto-generated catch block
1183 // e.printStackTrace();
1184 }
1185
1186 /*
1187 * Michael doesnt think the code below is really needed... it will just
1188 * slow things down when we are reading frames over a network***** for (;
1189 * num >= 0; num--) { System.out.println("This code is loading frames to
1190 * find the highest existing frame..."); if (LoadFrame(frameset + num) !=
1191 * null) break; }
1192 */
1193
1194 return num;
1195 }
1196
1197 /**
1198 * Checks if a given frameset is accessable.
1199 *
1200 * @param framesetName
1201 * @return
1202 */
1203 public static Boolean canAccessFrameset(String framesetName) {
1204 framesetName = framesetName.toLowerCase();
1205 for (String path : UserSettings.FrameDirs) {
1206 if ((new File(path + framesetName)).exists())
1207 return true;
1208 }
1209 return false;
1210 }
1211
1212 public static Frame CreateFrameset(String frameset, String path,
1213 boolean recreate) throws Exception {
1214 String conversion = frameset + " --> ";
1215
1216 if (!isValidFramesetName(frameset)) {
1217 throw new Exception("Invalid frameset name");
1218 }
1219
1220 if (!recreate && FrameIO.canAccessFrameset(frameset)) {
1221 throw new ExistingFramesetException(frameset);
1222 }
1223
1224 conversion += frameset;
1225 Logger.Log(Logger.SYSTEM, Logger.NEW_FRAMESET, "Frameset Name: "
1226 + conversion);
1227 conversion = frameset;
1228
1229 /**
1230 * TODO: Update this to exclude any\all invalid filename characters
1231 */
1232 // ignore annotation character
1233 if (frameset.startsWith("@"))
1234 frameset = frameset.substring(1);
1235
1236 conversion += " --> " + frameset;
1237 Logger.Log(Logger.SYSTEM, Logger.NEW_FRAMESET, "Name: " + conversion);
1238
1239 // create the new Frameset directory
1240 File dir = new File(path + frameset.toLowerCase() + File.separator);
1241
1242 dir.mkdirs();
1243
1244 // create the new INF file
1245 try {
1246 WriteINF(path, frameset, frameset + '1');
1247 } catch (IOException ioe) {
1248 ioe.printStackTrace();
1249 Logger.Log(ioe);
1250 }
1251
1252 SuspendCache();
1253 // copy the default .0 and .1 files
1254 Frame base = null;
1255 try {
1256 base = LoadFrame(UserSettings.DefaultFrame);
1257 } catch (Exception e) {
1258 }
1259 // The frame may not be accessed for various reasons... in all these
1260 // cases just create a new one
1261 if (base == null) {
1262 base = new Frame();
1263 }
1264
1265 ResumeCache();
1266
1267 base.reset();
1268 base.resetDateCreated();
1269 base.setFrameset(frameset);
1270 base.setFrameNumber(0);
1271 base.setTitle(base.getFramesetName() + "0");
1272 base.setPath(path);
1273 base.change();
1274 SaveFrame(base, false);
1275
1276 base.reset();
1277 base.resetDateCreated();
1278 base.setFrameNumber(1);
1279 base.setTitle(frameset);
1280 base.change();
1281 SaveFrame(base, true);
1282
1283 Logger.Log(Logger.SYSTEM, Logger.NEW_FRAMESET, "Created new frameset: "
1284 + frameset);
1285
1286 return base;
1287 }
1288
1289 /**
1290 * Tests if a frameset name is valid. That is it must begin and end with a
1291 * letter and contain only letters and digits in between.
1292 *
1293 * @param frameset
1294 * the name to be tested
1295 * @return true if the frameset name is valid
1296 */
1297 public static boolean isValidFramesetName(String frameset) {
1298 if (frameset == null) {
1299 return false;
1300 }
1301
1302 int nameLength = frameset.length();
1303 if (frameset.length() <= 0 || nameLength > MAX_NAME_LENGTH) {
1304 return false;
1305 }
1306
1307 int lastCharIndex = nameLength - 1;
1308
1309 if (!Character.isLetter(frameset.charAt(0))
1310 || !Character.isLetter(frameset.charAt(lastCharIndex)))
1311 return false;
1312
1313 for (int i = 1; i < lastCharIndex; i++) {
1314 if (!Character.isLetterOrDigit(frameset.charAt(i))) {
1315 return false;
1316 }
1317 }
1318 return true;
1319 }
1320
1321 public static boolean deleteFrameset(String framesetName) {
1322 return moveFrameset(framesetName, FrameIO.TRASH_PATH);
1323 }
1324
1325 public static boolean moveFrameset(String framesetName,
1326 String destinationFolder) {
1327 if (!FrameIO.canAccessFrameset(framesetName))
1328 return false;
1329 // Clear the cache
1330 _Cache.clear();
1331
1332 // Search all the available directories for the directory
1333 for (String path : UserSettings.FrameDirs) {
1334 String source = path + framesetName.toLowerCase() + File.separator;
1335 File framesetDirectory = new File(source);
1336 // Once we have found the directory move it
1337 if (framesetDirectory.exists()) {
1338 String destPath = destinationFolder
1339 + framesetName.toLowerCase();
1340 int copyNumber = 1;
1341 File dest = new File(destPath + File.separator);
1342 // Create the destination folder if it doesnt already exist
1343 if (!dest.getParentFile().exists())
1344 dest.mkdirs();
1345 // If a frameset with the same name is already in the
1346 // destination add
1347 // a number to the end
1348 while (dest.exists()) {
1349 dest = new File(destPath + ++copyNumber + File.separator);
1350 }
1351 if (!framesetDirectory.renameTo(dest)) {
1352 for (File f : framesetDirectory.listFiles()) {
1353 if (!f.delete())
1354 return false;
1355 }
1356 if (!framesetDirectory.delete())
1357 return false;
1358 }
1359 return true;
1360 }
1361 }
1362 return false;
1363 }
1364
1365 public static boolean CopyFrameset(String framesetToCopy,
1366 String copiedFrameset) throws Exception {
1367 if (!FrameIO.canAccessFrameset(framesetToCopy))
1368 return false;
1369 if (FrameIO.canAccessFrameset(copiedFrameset))
1370 return false;
1371 // search through all the directories to find the frameset we are
1372 // copying
1373 for (String path : UserSettings.FrameDirs) {
1374 String source = path + framesetToCopy.toLowerCase()
1375 + File.separator;
1376 File framesetDirectory = new File(source);
1377 if (framesetDirectory.exists()) {
1378 // copy the frameset
1379 File copyFramesetDirectory = new File(path
1380 + copiedFrameset.toLowerCase() + File.separator);
1381 if (!copyFramesetDirectory.mkdirs())
1382 return false;
1383 // copy each of the frames
1384 for (File f : framesetDirectory.listFiles()) {
1385 // Ignore hidden files
1386 if (f.getName().charAt(0) == '.')
1387 continue;
1388 String copyPath = copyFramesetDirectory.getAbsolutePath()
1389 + File.separator + f.getName();
1390 FrameIO.copyFile(f.getAbsolutePath(), copyPath);
1391 }
1392 return true;
1393 }
1394 }
1395 return false;
1396 }
1397
1398 /**
1399 * Copies a file from one location to another.
1400 *
1401 * @param existingFile
1402 * @param newFileName
1403 * @throws Exception
1404 */
1405 public static void copyFile(String existingFile, String newFileName)
1406 throws Exception {
1407 FileInputStream is = new FileInputStream(existingFile);
1408 FileOutputStream os = new FileOutputStream(newFileName, false);
1409 int data;
1410 while ((data = is.read()) != -1) {
1411 os.write(data);
1412 }
1413 os.flush();
1414 os.close();
1415 is.close();
1416 }
1417
1418 /**
1419 * Saves a frame regardless of whether or not the frame is marked as having
1420 * been changed.
1421 *
1422 * @param frame
1423 * the frame to save
1424 * @return the contents of the frame or null if it could not be saved
1425 */
1426 public static String ForceSaveFrame(Frame frame) {
1427 frame.change();
1428 return SaveFrame(frame, false);
1429 }
1430
1431 public static boolean isValidLink(String frameName) {
1432 return frameName == null || isPositiveInteger(frameName)
1433 || isValidFrameName(frameName);
1434 }
1435
1436 public static void SavePublicFrame(String peerName, String frameName,
1437 int version, BufferedReader packetContents) {
1438 // TODO handle versioning - add version to the header
1439 // Remote user uploads version based on an old version
1440
1441 // Remove it from the cache so that next time it is loaded we get the up
1442 // todate version
1443 _Cache.remove(frameName.toLowerCase());
1444
1445 // Save to file
1446 String filename = PUBLIC_PATH + Conversion.getFramesetName(frameName)
1447 + File.separator + Conversion.getFrameNumber(frameName)
1448 + ExpReader.EXTENTION;
1449
1450 File file = new File(filename);
1451 // Ensure the file exists
1452 if (file.exists()) {
1453 // Check the versions
1454 int savedVersion = ExpReader.getVersion(filename);
1455
1456 if (savedVersion > version) {
1457 // remove this frame from the cache if it is there
1458 // This will make sure links to the original are set correctly
1459 // _Cache.remove(frameName.toLowerCase());
1460
1461 int nextNum = 0;
1462 try {
1463 nextNum = ReadINF(PUBLIC_PATH, Conversion
1464 .getFramesetName(frameName), false) + 1;
1465 } catch (IOException e) {
1466 e.printStackTrace();
1467 }
1468
1469 String newName = Conversion.getFramesetName(frameName)
1470 + nextNum;
1471 filename = PUBLIC_PATH + Conversion.getFramesetName(frameName)
1472 + File.separator + nextNum + ExpReader.EXTENTION;
1473
1474 // Show the messages alerting the user
1475 Text originalMessage = new Text(-1);
1476 originalMessage.setColor(MessageBay.ERROR_COLOR);
1477 originalMessage.setText(frameName + " was edited by "
1478 + peerName);
1479 originalMessage.setLink(frameName);
1480 Text yourMessage = new Text(-1);
1481 yourMessage.setColor(MessageBay.ERROR_COLOR);
1482 yourMessage.setText("Their version was renamed " + newName);
1483 yourMessage.setLink(newName);
1484 MessageBay.displayMessage(originalMessage);
1485 MessageBay.displayMessage(yourMessage);
1486
1487 Frame editedFrame = FrameIO.LoadFrame(frameName);
1488
1489 FrameShare.getInstance().sendMessage(
1490 frameName + " was recently edited by "
1491 + editedFrame.getLastModifyUser(), peerName);
1492 FrameShare.getInstance().sendMessage(
1493 "Your version was renamed " + newName, peerName);
1494 }
1495 }
1496
1497 // Save the new version
1498 try {
1499 // FileWriter fw = new FileWriter(file);
1500
1501 // Open an Output Stream Writer to set encoding
1502 OutputStream fout = new FileOutputStream(file);
1503 OutputStream bout = new BufferedOutputStream(fout);
1504 Writer fw = new OutputStreamWriter(bout, "UTF-8");
1505
1506 String nextLine = null;
1507 while ((nextLine = packetContents.readLine()) != null) {
1508 fw.write(nextLine + '\n');
1509 }
1510 fw.flush();
1511 fw.close();
1512 MessageBay.displayMessage("Saved remote frame: " + frameName);
1513 } catch (IOException e) {
1514 MessageBay.errorMessage("Error remote saving " + frameName + ": "
1515 + e.getMessage());
1516 e.printStackTrace();
1517 }
1518 // } else {
1519 //
1520 //
1521 //
1522 // MessageBay
1523 // .errorMessage("Recieved save request for unknown public frame: "
1524 // + frameName);
1525 // }
1526 }
1527
1528 public static void setSavedProperties(Frame toSave) {
1529 toSave.setLastModifyDate(Formatter.getDateTime());
1530 toSave.setLastModifyUser(UserSettings.UserName);
1531 toSave.setVersion(toSave.getVersion() + 1);
1532 Time darkTime = new Time(SessionStats.getFrameDarkTime().getTime()
1533 + toSave.getDarkTime().getTime());
1534 Time activeTime = new Time(SessionStats.getFrameActiveTime().getTime()
1535 + toSave.getActiveTime().getTime());
1536 toSave.setDarkTime(darkTime);
1537 toSave.setActiveTime(activeTime);
1538 }
1539
1540}
Note: See TracBrowser for help on using the repository browser.