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

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

Renamed "FramesEdited.stats"
Added @NoSave tag

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.text.Format;
18import java.util.Collection;
19import java.util.HashMap;
20import java.util.LinkedList;
21
22import org.expeditee.actions.Actions;
23import org.expeditee.agents.ExistingFramesetException;
24import org.expeditee.io.Conversion;
25import org.expeditee.io.ExpReader;
26import org.expeditee.io.ExpWriter;
27import org.expeditee.io.FrameReader;
28import org.expeditee.io.FrameWriter;
29import org.expeditee.io.KMSReader;
30import org.expeditee.io.KMSWriter;
31import org.expeditee.io.Logger;
32import org.expeditee.items.Item;
33import org.expeditee.items.ItemUtils;
34import org.expeditee.items.Permission;
35import org.expeditee.items.Text;
36import org.expeditee.network.FrameShare;
37import org.expeditee.stats.SessionStats;
38
39/**
40 * This class provides static methods for all saving and loading of Frames
41 * to\from disk. This class also handles any caching of previously loaded
42 * Frames.
43 *
44 * @author jdm18
45 *
46 */
47public class FrameIO {
48
49 private static final char FRAME_NAME_LAST_CHAR = 'A';
50
51 public static void changeParentFolder(String newFolder) {
52 PARENT_FOLDER = newFolder;
53 PUBLIC_PATH = PARENT_FOLDER + "public" + File.separator;
54 FRAME_PATH = PARENT_FOLDER + "framesets" + File.separator;
55 MESSAGES_PATH = PARENT_FOLDER + "messages" + File.separator;
56 TRASH_PATH = PARENT_FOLDER + "trash" + File.separator;
57 IMAGES_PATH = PARENT_FOLDER + IMAGES_FOLDER;
58 HELP_PATH = PARENT_FOLDER + "documentation" + File.separator;
59 PROFILE_PATH = PARENT_FOLDER + "profiles" + File.separator;
60 EXPORTS_DIR = PARENT_FOLDER + "exports" + File.separator;
61 STATISTICS_DIR = PARENT_FOLDER + "statistics" + File.separator;
62 LOGS_DIR = PARENT_FOLDER + "logs" + File.separator;
63 }
64
65 /**
66 * The default location for storing the framesets. Each frameset has its own
67 * subdirectory in this directory.
68 */
69 public static String IMAGES_FOLDER = "images" + File.separator;
70
71 public static String TRASH_PATH;
72
73 public static String PARENT_FOLDER;
74
75 public static String FRAME_PATH;
76
77 public static String MESSAGES_PATH;
78
79 public static String PUBLIC_PATH;
80
81 public static String IMAGES_PATH;
82
83 public static String HELP_PATH;
84
85 public static String PROFILE_PATH;
86
87 public static String EXPORTS_DIR;
88
89 public static String STATISTICS_DIR;
90
91 public static String LOGS_DIR;
92
93 private static final String INF_FILENAME = "frame.inf";
94
95 public static final String ILLEGAL_CHARS = ";:\\/?";
96
97 public static final int MAX_NAME_LENGTH = 64;
98
99 public static final int MAX_CACHE = 100;
100
101 private static HashMap<String, Frame> _Cache = new FrameCache();
102
103 // private static HashMap<String, String> _FramesetNameCache = new
104 // HashMap<String, String>();
105
106 private static boolean ENABLE_CACHE = true;
107
108 private static boolean _UseCache = true;
109
110 private static boolean _SuspendedCache = false;
111
112 // All methods are static, this should not be instantiated
113 private FrameIO() {
114 }
115
116 public static boolean isCacheOn() {
117 return _UseCache && ENABLE_CACHE;
118 }
119
120 public static void Precache(String framename) {
121 // if the cache is turned off, do nothing
122 if (!isCacheOn())
123 return;
124
125 // if the frame is already in the cache, do nothing
126 if (_Cache.containsKey(framename.toLowerCase()))
127 return;
128
129 // otherwise, load the frame and put it in the cache
130 Logger.Log(Logger.SYSTEM, Logger.LOAD, "Precaching " + framename + ".");
131
132 // do not display errors encountered to the user
133 // (they will be shown at load time)
134 MessageBay.supressMessages(true);
135 // loading automatically caches the frame is caching is turned on
136 LoadFromDisk(framename, null, false);
137 MessageBay.supressMessages(false);
138 }
139
140 /**
141 * Checks if a string is a representation of a positive integer.
142 *
143 * @param s
144 * @return true if s is a positive integer
145 */
146 public static boolean isPositiveInteger(String s) {
147 if (s == null || s.length() == 0)
148 return false;
149
150 for (int i = 0; i < s.length(); i++) {
151 if (!Character.isDigit(s.charAt(i)))
152 return false;
153 }
154 return true;
155 }
156
157 public static Frame LoadFrame(String frameName) {
158 return LoadFrame(frameName, null, false);
159 }
160
161 public static Frame LoadFrame(String frameName, String path) {
162 return LoadFrame(frameName, path, false);
163 }
164
165 public static Frame LoadFrame(String frameName, String path,
166 boolean ignoreAnnotations) {
167 if (!isValidFrameName(frameName))
168 return null;
169
170 String frameNameLower = frameName.toLowerCase();
171 // first try reading from cache
172 if (isCacheOn() && _Cache.containsKey(frameNameLower)) {
173 Logger.Log(Logger.SYSTEM, Logger.LOAD, "Loading " + frameName
174 + " from cache.");
175 Frame frame = _Cache.get(frameNameLower);
176 return frame;
177 }
178
179 Logger.Log(Logger.SYSTEM, Logger.LOAD, "Loading " + frameName
180 + " from disk.");
181
182 return LoadFromDisk(frameName, path, ignoreAnnotations);
183 }
184
185 public static BufferedReader LoadPublicFrame(String frameName) {
186 String fullPath = FrameIO.getFrameFullPathName(PUBLIC_PATH, frameName);
187
188 if (fullPath == null)
189 return null;
190
191 File frameFile = new File(fullPath);
192 if (frameFile.exists() && frameFile.canRead()) {
193 try {
194 return new BufferedReader(new FileReader(frameFile));
195 } catch (FileNotFoundException e) {
196 e.printStackTrace();
197 }
198 }
199 return null;
200 }
201
202 private static Frame LoadFromDisk(String framename, String knownPath,
203 boolean ignoreAnnotations) {
204 Frame loaded = null;
205
206 if (knownPath != null) {
207 loaded = LoadKnowPath(knownPath, framename);
208 } else {
209
210 for (String path : UserSettings.FrameDirs) {
211 loaded = LoadKnowPath(path, framename);
212 if (loaded != null) {
213 break;
214 }
215 }
216 }
217
218 if (loaded == null && FrameShare.getInstance() != null) {
219 loaded = FrameShare.getInstance().loadFrame(framename, knownPath);
220 }
221
222 if (loaded != null) {
223 FrameUtils.Parse(loaded, true, ignoreAnnotations);
224 }
225
226 return loaded;
227 }
228
229 /**
230 * Gets a list of all the framesets available to the user
231 *
232 * @return a string containing a list of all the available framesets on
233 * separate lines
234 */
235 public static String getFramesetList() {
236 StringBuffer list = new StringBuffer();
237
238 for (String path : UserSettings.FrameDirs) {
239 File files = new File(path);
240 if (!files.exists())
241 continue;
242 for (File f : (new File(path)).listFiles()) {
243 if (f.isDirectory()) {
244 list.append(f.getName()).append('\n');
245 }
246 }
247 }
248 // remove the final new line char
249 list.deleteCharAt(list.length() - 1);
250 return list.toString();
251 }
252
253 /**
254 * Gets the full path and file name of the frame.
255 *
256 * @param path-
257 * the directory in which to look for the frameset containing the
258 * frame.
259 * @param frameName-
260 * the name of the frame for which the path is being requested.
261 * @return null if the frame can not be located.
262 */
263 public static synchronized String getFrameFullPathName(String path,
264 String frameName) {
265 String source = path + Conversion.getFramesetName(frameName)
266 + File.separator;
267
268 File tester = new File(source);
269 if (!tester.exists())
270 return null;
271
272 // check for the new file name format
273 String fullPath = source + Conversion.getFrameNumber(frameName)
274 + ExpReader.EXTENTION;
275 tester = new File(fullPath);
276
277 if (tester.exists())
278 return fullPath;
279
280 // check for oldfile name format
281 fullPath = source + Conversion.getFramesetName(frameName) + "."
282 + Conversion.getFrameNumber(frameName);
283 tester = new File(fullPath);
284
285 if (tester.exists())
286 return fullPath;
287
288 return null;
289 }
290
291 public static boolean canAccessFrame(String frameName) {
292 Frame current = DisplayIO.getCurrentFrame();
293 // Just incase the current frame is not yet saved...
294 if (frameName.equals(current.getName())) {
295 FrameIO.SaveFrame(current, false, false);
296 current.change();
297 return true;
298 }
299
300 for (String path : UserSettings.FrameDirs) {
301 if (getFrameFullPathName(path, frameName) != null)
302 return true;
303 }
304 return false;
305 }
306
307 public static Collection<String> searchFrame(String frameName,
308 String pattern, String path) {
309 String fullPath = null;
310 if (path == null) {
311 for (String possiblePath : UserSettings.FrameDirs) {
312 fullPath = getFrameFullPathName(possiblePath, frameName);
313 if (fullPath != null)
314 break;
315 }
316 } else {
317 fullPath = getFrameFullPathName(path, frameName);
318 }
319 // If the frame was not located return null
320 if (fullPath == null)
321 return null;
322 Collection<String> results = new LinkedList<String>();
323 // Open the file and search the text items
324 try {
325 BufferedReader reader = new BufferedReader(new FileReader(fullPath));
326 String next;
327 while (reader.ready() && ((next = reader.readLine()) != null)) {
328 if (next.startsWith("T")) {
329 String toSearch = next.substring(2);
330 if (toSearch.toLowerCase().contains(pattern))
331 results.add(toSearch);
332 }
333 }
334 } catch (FileNotFoundException e) {
335 e.printStackTrace();
336 return null;
337 } catch (IOException e) {
338 e.printStackTrace();
339 }
340 return results;
341 }
342
343 private static Frame LoadKnowPath(String path, String frameName) {
344 String fullPath = getFrameFullPathName(path, frameName);
345 if (fullPath == null)
346 return null;
347
348 try {
349 FrameReader reader;
350
351 if (fullPath.endsWith(ExpReader.EXTENTION)) {
352 reader = new ExpReader(frameName);
353 } else {
354 reader = new KMSReader();
355 }
356 Frame frame = reader.readFrame(fullPath);
357
358 if (frame == null) {
359 MessageBay.errorMessage("Error: " + frameName
360 + " could not be successfully loaded.");
361 return null;
362 }
363
364 frame.setPath(path);
365
366 // do not put 0 frames or virtual frames into the cache
367 // Why are zero frames not put in the cache
368 if (_Cache.size() > MAX_CACHE)
369 _Cache.clear();
370
371 if (frame.getNumber() > 0 && isCacheOn())
372 _Cache.put(frameName.toLowerCase(), frame);
373
374 return frame;
375 } catch (IOException ioe) {
376 ioe.printStackTrace();
377 Logger.Log(ioe);
378 } catch (Exception e) {
379 e.printStackTrace();
380 Logger.Log(e);
381 MessageBay.errorMessage("Error: " + frameName
382 + " could not be successfully loaded.");
383 }
384
385 return null;
386 }
387
388 public static void Reload() {
389 // disable cache
390 boolean cache = _UseCache;
391
392 _UseCache = false;
393 Frame fresh = FrameIO.LoadFrame(DisplayIO.getCurrentFrame().getName());
394 _UseCache = cache;
395 if (_Cache.containsKey(fresh.getName().toLowerCase()))
396 addToCache(fresh);
397 DisplayIO.setCurrentFrame(fresh, false);
398 }
399
400 public static Frame LoadPrevious(Frame current) {
401 checkTDFC(current);
402
403 // the current name and number
404 String name = current.getFramesetName();
405 int num = current.getNumber() - 1;
406
407 // loop until a frame that exists is found
408 for (; num >= 0; num--) {
409 Frame f = LoadFrame(name + num, current.getPath());
410 if (f != null)
411 return f;
412 }
413
414 // if we did not find another Frame then this one must be the last one
415 // in the frameset
416 MessageBay
417 .displayMessageOnce("This is the first frame in the frameset");
418 return null;
419 }
420
421 /**
422 * Returns the next Frame in the current Frameset (The Frame with the next
423 * highest Frame number) If the current Frame is the last one in the
424 * Frameset, or an error occurs then null is returned.
425 *
426 * @return The Frame after this one in the current frameset, or null
427 */
428 public static Frame LoadNext(Frame current) {
429 checkTDFC(current);
430
431 // the current name and number
432 int num = current.getNumber() + 1;
433 int max = num + 1;
434 String name = current.getFramesetName();
435
436 // read the maximum from the INF file
437 try {
438 max = ReadINF(current.getPath(), current.getFramesetName(), false);
439 } catch (IOException ioe) {
440 MessageBay.errorMessage("Error loading INF file for frameset '"
441 + name + "'");
442 return null;
443 }
444
445 // loop until a frame that exists is found
446 for (; num <= max; num++) {
447 Frame f = LoadFrame(name + num, current.getPath());
448 if (f != null)
449 return f;
450 }
451
452 // if we did not find another Frame then this one must be the last one
453 // in the frameset
454 MessageBay.displayMessageOnce("This is the last frame in the frameset");
455 return null;
456 }
457
458 /**
459 * This method checks if the current frame has just been created with TDFC.
460 * If it has the frame is saved regardless of whether it has been edited or
461 * not and the TDFC item property is cleared. This is to ensure that the
462 * link is saved on the parent frame.
463 *
464 * @param current
465 */
466 public static void checkTDFC(Frame current) {
467 if (FrameUtils.getTdfcItem() != null) {
468 FrameUtils.setTdfcItem(null);
469 current.change();
470 }
471 }
472
473 public static Frame LoadLast(String framesetName, String path) {
474 // read the maximum from the INF file
475 int max;
476 try {
477 max = ReadINF(path, framesetName, false);
478 } catch (IOException ioe) {
479 MessageBay.errorMessage("Error loading INF file for frameset '"
480 + framesetName + "'");
481 return null;
482 }
483
484 // loop backwards until a frame that exists is found
485 for (int num = max; num > 0; num--) {
486 Frame f = LoadFromDisk(framesetName + num, path, false);
487 if (f != null)
488 return f;
489 }
490
491 // if we did not find another Frame then this one must be the last one
492 // in the frameset
493 MessageBay.displayMessage("This is the last frame in the frameset");
494 return null;
495 }
496
497 public static Frame LoadZero(String framesetName, String path) {
498 return LoadFrame(framesetName + 0);
499 }
500
501 public static Frame LoadZero() {
502 Frame current = DisplayIO.getCurrentFrame();
503 return LoadZero(current.getFramesetName(), current.getPath());
504 }
505
506 public static Frame LoadLast() {
507 Frame current = DisplayIO.getCurrentFrame();
508 return LoadLast(current.getFramesetName(), current.getPath());
509 }
510
511 public static Frame LoadNext() {
512 return LoadNext(DisplayIO.getCurrentFrame());
513 }
514
515 public static Frame LoadPrevious() {
516 return LoadPrevious(DisplayIO.getCurrentFrame());
517 }
518
519 /**
520 * Deletes the given Frame on disk and removes the cached Frame if there is
521 * one. Also adds the deleted frame into the deletedFrames frameset.
522 *
523 * @param toDelete
524 * The Frame to be deleted
525 * @return The name the deleted frame was changed to, or null if the delete
526 * failed
527 */
528 public static String DeleteFrame(Frame toDelete) throws IOException,
529 SecurityException {
530 if (toDelete == null)
531 return null;
532
533 // Dont delete the zero frame
534 if (toDelete.getNumber() == 0) {
535 throw new SecurityException("Deleting a zero frame is illegal");
536 }
537
538 // Dont delete the zero frame
539 if (!toDelete.isLocal()) {
540 throw new SecurityException("Attempted to delete remote frame");
541 }
542
543 SaveFrame(toDelete);
544
545 // Copy deleted frames to the DeletedFrames frameset
546 // get the last used frame in the destination frameset
547 final String DELETED_FRAMES = "DeletedFrames";
548 int lastNumber = FrameIO.getLastNumber(DELETED_FRAMES);
549 String framePath;
550 try {
551 // create the new frameset
552 Frame one = FrameIO.CreateFrameset(DELETED_FRAMES, toDelete
553 .getPath());
554 framePath = one.getPath();
555 lastNumber = 0;
556 } catch (Exception e) {
557 Frame zero = FrameIO.LoadFrame(DELETED_FRAMES + "0");
558 framePath = zero.getPath();
559 }
560
561 // get the fill path to determine which file version it is
562 String source = getFrameFullPathName(toDelete.getPath(), toDelete
563 .getName());
564
565 String oldFrameName = toDelete.getName().toLowerCase();
566 // Now save the frame in the new location
567 toDelete.setFrameset(DELETED_FRAMES);
568 toDelete.setFrameNumber(lastNumber + 1);
569 toDelete.setPath(framePath);
570 ForceSaveFrame(toDelete);
571
572 if (_Cache.containsKey(oldFrameName))
573 _Cache.remove(oldFrameName);
574
575 File del = new File(source);
576
577 java.io.FileInputStream ff = new java.io.FileInputStream(del);
578 ff.close();
579
580 if (del.delete()) {
581 return toDelete.getName();
582 }
583
584 return null;
585 }
586
587 /**
588 * Creates a new Frame in the given frameset and assigns it the given Title,
589 * which can be null. The newly created Frame is a copy of the frameset's .0
590 * file with the number updated based on the last recorded Frame name in the
591 * frameset's INF file.
592 *
593 * @param frameset
594 * The frameset to create the new Frame in
595 * @param frameTitle
596 * The title to assign to the newly created Frame (can be NULL).
597 * @return The newly created Frame.
598 */
599 public static synchronized Frame CreateFrame(String frameset,
600 String frameTitle, String templateFrame) throws RuntimeException {
601
602 if (!FrameIO.isValidFramesetName(frameset)) {
603 throw new RuntimeException(frameset
604 + " is not a valid frameset name");
605 }
606
607 int next = -1;
608
609 // disable caching of 0 frames
610 // Mike says: Why is caching of 0 frames being disabled?
611 /*
612 * Especially since 0 frames are not event put into the cache in the
613 * frist place
614 */
615 // SuspendCache();
616 /*
617 * Suspending the cache causes infinate loops when trying to load a zero
618 * frame which has a ao which contains an v or av which contains a link
619 * to the ao frame
620 */
621
622 String zeroFrameName = frameset + "0";
623 Frame destFramesetZero = LoadFrame(zeroFrameName);
624 if (destFramesetZero == null) {
625 throw new RuntimeException(zeroFrameName + " could not be found");
626 }
627
628 Frame template = null;
629 if (templateFrame == null) {
630 // load in frame.0
631 template = destFramesetZero;
632 } else {
633 template = LoadFrame(templateFrame);
634 if (template == null) {
635 throw new RuntimeException("LinkTemplate " + templateFrame
636 + " could not be found");
637 }
638 }
639
640 ResumeCache();
641
642 // read the next number from the INF file
643 try {
644 next = ReadINF(destFramesetZero.getPath(), frameset, true);
645 } catch (IOException ioe) {
646 ioe.printStackTrace();
647 throw new RuntimeException("INF file could not be read");
648 }
649
650 // Remove the old frame from the cashe then add the new one
651 // TODO figure out some way that we can put both in the cache
652 _Cache.remove(template.getName().toLowerCase());
653 // set the number and title of the new frame
654 template.setName(frameset, ++next);
655 template.setTitle(frameTitle);
656 // _Cache.put(template.getName().toLowerCase(), template);
657
658 Logger.Log(Logger.SYSTEM, Logger.TDFC, "Creating new frame: "
659 + template.getName() + " from TDFC");
660
661 template.setOwner(UserSettings.UserName);
662 template.resetDateCreated();
663 for (Item i : template.getItems()) {
664 if (ItemUtils.startsWithTag(i, ItemUtils.TAG_PARENT))
665 i.setLink(null);
666 }
667
668 // do auto shrinking of the title IF not in twin frames mode
669 Item titleItem = template.getTitleItem();
670
671 if (!DisplayIO.isTwinFramesOn()) {
672 if ((titleItem.getX() + 1) < template.getNameItem().getX()) {
673 while (titleItem.getSize() > Text.MINIMUM_FONT_SIZE
674 && titleItem.getBoundsWidth() + titleItem.getX() > template
675 .getNameItem().getX()) {
676 titleItem.setSize(titleItem.getSize() - 1);
677
678 }
679 } else {
680 System.out.println("Bad title x position: " + titleItem.getX());
681 }
682 }
683
684 // Assign a width to the title.
685 titleItem.setRightMargin(template.getNameItem().getX(), true);
686
687 return template;
688 }
689
690 public static void DisableCache() {
691 _UseCache = false;
692 }
693
694 public static void EnableCache() {
695 _UseCache = true;
696 }
697
698 public static void SuspendCache() {
699 if (_UseCache) {
700 DisableCache();
701 _SuspendedCache = true;
702 } else {
703 _SuspendedCache = false;
704 }
705 }
706
707 public static void ResumeCache() {
708 if (_SuspendedCache) {
709 EnableCache();
710 _SuspendedCache = false;
711 }
712 }
713
714 public static void RefreshCasheImages() {
715 SuspendCache();
716 for (Frame f : _Cache.values())
717 f.setBuffer(null);
718 ResumeCache();
719 }
720
721 /**
722 * Creates a new frameset using the given name. This includes creating a new
723 * subdirectory in the <code>FRAME_PATH</code> directory, Copying over the
724 * default.0 frame from the default frameset, copying the .0 Frame to make a
725 * .1 Frame, and creating the frameset's INF file.
726 *
727 * @param frameset
728 * The name of the Frameset to create
729 * @return The first Frame of the new Frameset (Frame.1)
730 */
731 public static Frame CreateFrameset(String frameset, String path)
732 throws Exception {
733 return CreateFrameset(frameset, path, false);
734 }
735
736 /**
737 * Tests if the given String is a 'proper' framename, that is, the String
738 * must begin with a character, end with a number with 0 or more letters and
739 * numbers in between.
740 *
741 * @param frameName
742 * The String to test for validity as a frame name
743 * @return True if the given framename is proper, false otherwise.
744 */
745 public static boolean isValidFrameName(String frameName) {
746
747 if (frameName == null || frameName.length() < 2)
748 return false;
749
750 int lastCharIndex = frameName.length() - 1;
751 // String must begin with a letter and end with a digit
752 if (!Character.isLetter(frameName.charAt(0))
753 || !Character.isDigit(frameName.charAt(lastCharIndex)))
754 return false;
755
756 // All the characters between first and last must be letters
757 // or digits
758 for (int i = 1; i < lastCharIndex; i++) {
759 if (!Character.isLetterOrDigit(frameName.charAt(i)))
760 return false;
761 }
762 return true;
763 }
764
765 /**
766 * Saves the given Frame to disk in the corresponding frameset directory.
767 * This is the same as calling SaveFrame(toSave, true)
768 *
769 * @param toSave
770 * The Frame to save to disk
771 */
772 public static String SaveFrame(Frame toSave) {
773 return SaveFrame(toSave, true);
774 }
775
776 /**
777 * Saves a frame.
778 *
779 * @param toSave
780 * the frame to save
781 * @param inc
782 * true if the frames counter should be incremented
783 * @return the text content of the frame
784 */
785 public static String SaveFrame(Frame toSave, boolean inc) {
786 return SaveFrame(toSave, inc, true);
787 }
788
789 /**
790 * Saves the given Frame to disk in the corresponding frameset directory, if
791 * inc is true then the saved frames counter is incremented, otherwise it is
792 * untouched.
793 *
794 * @param toSave
795 * The Frame to save to disk
796 * @param inc
797 * True if the saved frames counter should be incremented, false
798 * otherwise.
799 * @param checkBackup
800 * True if the frame should be checked for the back up tag
801 */
802 public static String SaveFrame(Frame toSave, boolean inc,
803 boolean checkBackup) {
804
805 // TODO When loading a frame maybe append onto the event history too-
806 // with a
807 // break to indicate the end of a session
808
809 if (toSave == null || !toSave.hasChanged() || toSave.isSaved()) {
810 SessionStats.NewFrameSession();
811 return "";
812 }
813
814 // Dont save if the frame is protected and it exists
815 if (checkBackup && toSave.isReadOnly()) {
816 _Cache.remove(toSave.getName().toLowerCase());
817 SessionStats.NewFrameSession();
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.resetDateCreated();
1268 base.setFrameset(frameset);
1269 base.setFrameNumber(0);
1270 base.setTitle(base.getFramesetName() + "0");
1271 base.setPath(path);
1272 base.change();
1273 SaveFrame(base, false);
1274
1275 base.resetDateCreated();
1276 base.setFrameNumber(1);
1277 base.setTitle(frameset);
1278 base.change();
1279 SaveFrame(base, true);
1280
1281 Logger.Log(Logger.SYSTEM, Logger.NEW_FRAMESET, "Created new frameset: "
1282 + frameset);
1283
1284 return base;
1285 }
1286
1287 /**
1288 * Tests if a frameset name is valid. That is it must begin and end with a
1289 * letter and contain only letters and digits in between.
1290 *
1291 * @param frameset
1292 * the name to be tested
1293 * @return true if the frameset name is valid
1294 */
1295 public static boolean isValidFramesetName(String frameset) {
1296 if (frameset == null) {
1297 return false;
1298 }
1299
1300 int nameLength = frameset.length();
1301 if (frameset.length() <= 0 || nameLength > MAX_NAME_LENGTH) {
1302 return false;
1303 }
1304
1305 int lastCharIndex = nameLength - 1;
1306
1307 if (!Character.isLetter(frameset.charAt(0))
1308 || !Character.isLetter(frameset.charAt(lastCharIndex)))
1309 return false;
1310
1311 for (int i = 1; i < lastCharIndex; i++) {
1312 if (!Character.isLetterOrDigit(frameset.charAt(i))) {
1313 return false;
1314 }
1315 }
1316 return true;
1317 }
1318
1319 public static boolean deleteFrameset(String framesetName) {
1320 return moveFrameset(framesetName, FrameIO.TRASH_PATH);
1321 }
1322
1323 public static boolean moveFrameset(String framesetName,
1324 String destinationFolder) {
1325 if (!FrameIO.canAccessFrameset(framesetName))
1326 return false;
1327 // Clear the cache
1328 _Cache.clear();
1329
1330 // Search all the available directories for the directory
1331 for (String path : UserSettings.FrameDirs) {
1332 String source = path + framesetName.toLowerCase() + File.separator;
1333 File framesetDirectory = new File(source);
1334 // Once we have found the directory move it
1335 if (framesetDirectory.exists()) {
1336 String destPath = destinationFolder
1337 + framesetName.toLowerCase();
1338 int copyNumber = 1;
1339 File dest = new File(destPath + File.separator);
1340 // Create the destination folder if it doesnt already exist
1341 if (!dest.getParentFile().exists())
1342 dest.mkdirs();
1343 // If a frameset with the same name is already in the
1344 // destination add
1345 // a number to the end
1346 while (dest.exists()) {
1347 dest = new File(destPath + ++copyNumber + File.separator);
1348 }
1349 if (!framesetDirectory.renameTo(dest)) {
1350 for (File f : framesetDirectory.listFiles()) {
1351 if (!f.delete())
1352 return false;
1353 }
1354 if (!framesetDirectory.delete())
1355 return false;
1356 }
1357 return true;
1358 }
1359 }
1360 return false;
1361 }
1362
1363 public static boolean CopyFrameset(String framesetToCopy,
1364 String copiedFrameset) throws Exception {
1365 if (!FrameIO.canAccessFrameset(framesetToCopy))
1366 return false;
1367 if (FrameIO.canAccessFrameset(copiedFrameset))
1368 return false;
1369 // search through all the directories to find the frameset we are
1370 // copying
1371 for (String path : UserSettings.FrameDirs) {
1372 String source = path + framesetToCopy.toLowerCase()
1373 + File.separator;
1374 File framesetDirectory = new File(source);
1375 if (framesetDirectory.exists()) {
1376 // copy the frameset
1377 File copyFramesetDirectory = new File(path
1378 + copiedFrameset.toLowerCase() + File.separator);
1379 if (!copyFramesetDirectory.mkdirs())
1380 return false;
1381 // copy each of the frames
1382 for (File f : framesetDirectory.listFiles()) {
1383 // Ignore hidden files
1384 if (f.getName().charAt(0) == '.')
1385 continue;
1386 String copyPath = copyFramesetDirectory.getAbsolutePath()
1387 + File.separator + f.getName();
1388 FrameIO.copyFile(f.getAbsolutePath(), copyPath);
1389 }
1390 return true;
1391 }
1392 }
1393 return false;
1394 }
1395
1396 /**
1397 * Copies a file from one location to another.
1398 *
1399 * @param existingFile
1400 * @param newFileName
1401 * @throws Exception
1402 */
1403 public static void copyFile(String existingFile, String newFileName)
1404 throws Exception {
1405 FileInputStream is = new FileInputStream(existingFile);
1406 FileOutputStream os = new FileOutputStream(newFileName, false);
1407 int data;
1408 while ((data = is.read()) != -1) {
1409 os.write(data);
1410 }
1411 os.flush();
1412 os.close();
1413 is.close();
1414 }
1415
1416 /**
1417 * Saves a frame regardless of whether or not the frame is marked as having
1418 * been changed.
1419 *
1420 * @param frame
1421 * the frame to save
1422 * @return the contents of the frame or null if it could not be saved
1423 */
1424 public static String ForceSaveFrame(Frame frame) {
1425 frame.change();
1426 return SaveFrame(frame, false);
1427 }
1428
1429 public static boolean isValidLink(String frameName) {
1430 return frameName == null || isPositiveInteger(frameName)
1431 || isValidFrameName(frameName);
1432 }
1433
1434 public static void SavePublicFrame(String peerName, String frameName,
1435 int version, BufferedReader packetContents) {
1436 // TODO handle versioning - add version to the header
1437 // Remote user uploads version based on an old version
1438
1439 // Remove it from the cache so that next time it is loaded we get the up
1440 // todate version
1441 _Cache.remove(frameName.toLowerCase());
1442
1443 // Save to file
1444 String filename = PUBLIC_PATH + Conversion.getFramesetName(frameName)
1445 + File.separator + Conversion.getFrameNumber(frameName)
1446 + ExpReader.EXTENTION;
1447
1448 File file = new File(filename);
1449 // Ensure the file exists
1450 if (file.exists()) {
1451 // Check the versions
1452 int savedVersion = ExpReader.getVersion(filename);
1453
1454 if (savedVersion > version) {
1455 // remove this frame from the cache if it is there
1456 // This will make sure links to the original are set correctly
1457 // _Cache.remove(frameName.toLowerCase());
1458
1459 int nextNum = 0;
1460 try {
1461 nextNum = ReadINF(PUBLIC_PATH, Conversion
1462 .getFramesetName(frameName), false) + 1;
1463 } catch (IOException e) {
1464 e.printStackTrace();
1465 }
1466
1467 String newName = Conversion.getFramesetName(frameName)
1468 + nextNum;
1469 filename = PUBLIC_PATH + Conversion.getFramesetName(frameName)
1470 + File.separator + nextNum + ExpReader.EXTENTION;
1471
1472 // Show the messages alerting the user
1473 Text originalMessage = new Text(-1);
1474 originalMessage.setColor(MessageBay.ERROR_COLOR);
1475 originalMessage.setText(frameName + " was edited by "
1476 + peerName);
1477 originalMessage.setLink(frameName);
1478 Text yourMessage = new Text(-1);
1479 yourMessage.setColor(MessageBay.ERROR_COLOR);
1480 yourMessage.setText("Their version was renamed " + newName);
1481 yourMessage.setLink(newName);
1482 MessageBay.displayMessage(originalMessage);
1483 MessageBay.displayMessage(yourMessage);
1484
1485 Frame editedFrame = FrameIO.LoadFrame(frameName);
1486
1487 FrameShare.getInstance().sendMessage(
1488 frameName + " was recently edited by "
1489 + editedFrame.getLastModifyUser(), peerName);
1490 FrameShare.getInstance().sendMessage(
1491 "Your version was renamed " + newName, peerName);
1492 }
1493 }
1494
1495 // Save the new version
1496 try {
1497 // FileWriter fw = new FileWriter(file);
1498
1499 // Open an Output Stream Writer to set encoding
1500 OutputStream fout = new FileOutputStream(file);
1501 OutputStream bout = new BufferedOutputStream(fout);
1502 Writer fw = new OutputStreamWriter(bout, "UTF-8");
1503
1504 String nextLine = null;
1505 while ((nextLine = packetContents.readLine()) != null) {
1506 fw.write(nextLine + '\n');
1507 }
1508 fw.flush();
1509 fw.close();
1510 MessageBay.displayMessage("Saved remote frame: " + frameName);
1511 } catch (IOException e) {
1512 MessageBay.errorMessage("Error remote saving " + frameName + ": "
1513 + e.getMessage());
1514 e.printStackTrace();
1515 }
1516 // } else {
1517 //
1518 //
1519 //
1520 // MessageBay
1521 // .errorMessage("Recieved save request for unknown public frame: "
1522 // + frameName);
1523 // }
1524 }
1525
1526 public static void setSavedProperties(Frame toSave) {
1527 toSave.setLastModifyDate(Logger.EasyDateFormat("ddMMMyyyy:HHmm"));
1528 toSave.setLastModifyUser(UserSettings.UserName);
1529 toSave.setVersion(toSave.getVersion() + 1);
1530 Time darkTime = new Time(SessionStats.getFrameDarkTime().getTime()
1531 + toSave.getDarkTime().getTime());
1532 Time activeTime = new Time(SessionStats.getFrameActiveTime().getTime()
1533 + toSave.getActiveTime().getTime());
1534 toSave.setDarkTime(darkTime);
1535 toSave.setActiveTime(activeTime);
1536 }
1537
1538}
Note: See TracBrowser for help on using the repository browser.