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

Last change on this file since 1277 was 1277, checked in by bln4, 5 years ago

Changes have been made to how mail is stored. Each user now has a location in their deaddrops folder (found inside their resources folder) that acts as an exchange location for each of their partnerships. Mail databases now belong in there.

Initial functionality to support the culling of already processed messages from the mail database has been implemented with more to follow.

File size: 58.5 KB
Line 
1/**
2 * FrameIO.java
3 * Copyright (C) 2010 New Zealand Digital Library, http://expeditee.org
4 *
5 * This program is free software: you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation, either version 3 of the License, or
8 * (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program. If not, see <http://www.gnu.org/licenses/>.
17 */
18
19package org.expeditee.gui;
20
21import java.io.BufferedOutputStream;
22import java.io.BufferedReader;
23import java.io.BufferedWriter;
24import java.io.File;
25import java.io.FileInputStream;
26import java.io.FileNotFoundException;
27import java.io.FileOutputStream;
28import java.io.FileReader;
29import java.io.FileWriter;
30import java.io.IOException;
31import java.io.OutputStream;
32import java.io.OutputStreamWriter;
33import java.io.Writer;
34import java.nio.channels.FileChannel;
35import java.nio.file.Files;
36import java.nio.file.Path;
37import java.nio.file.Paths;
38import java.sql.Time;
39import java.util.Arrays;
40import java.util.Collection;
41import java.util.HashMap;
42import java.util.LinkedList;
43import java.util.List;
44import java.util.Map;
45import java.util.function.Consumer;
46import java.util.stream.Collectors;
47
48import org.apollo.io.AudioPathManager;
49import org.expeditee.actions.Actions;
50import org.expeditee.agents.ExistingFramesetException;
51import org.expeditee.agents.InvalidFramesetNameException;
52import org.expeditee.auth.Authenticator;
53import org.expeditee.auth.EncryptedExpReader;
54import org.expeditee.auth.EncryptedExpWriter;
55import org.expeditee.auth.gui.MailBay;
56import org.expeditee.gio.EcosystemManager;
57import org.expeditee.io.Conversion;
58import org.expeditee.io.ExpReader;
59import org.expeditee.io.ExpWriter;
60import org.expeditee.io.FrameReader;
61import org.expeditee.io.FrameWriter;
62import org.expeditee.io.KMSReader;
63import org.expeditee.io.KMSWriter;
64import org.expeditee.items.Item;
65import org.expeditee.items.ItemUtils;
66import org.expeditee.items.Justification;
67import org.expeditee.items.PermissionPair;
68import org.expeditee.items.Text;
69import org.expeditee.items.UserAppliedPermission;
70import org.expeditee.network.FrameShare;
71import org.expeditee.setting.Setting;
72import org.expeditee.settings.UserSettings;
73import org.expeditee.settings.folders.FolderSettings;
74import org.expeditee.settings.templates.TemplateSettings;
75import org.expeditee.stats.Formatter;
76import org.expeditee.stats.Logger;
77import org.expeditee.stats.SessionStats;
78
79/**
80 * This class provides static methods for all saving and loading of Frames
81 * to\from disk. This class also handles any caching of previously loaded
82 * Frames.
83 *
84 * @author jdm18
85 *
86 */
87
88
89public class FrameIO {
90
91 private static final char FRAME_NAME_LAST_CHAR = 'A';
92
93 // The parent path that all others are relative to. Also referred to as Expeditee Home.
94 public static String PARENT_FOLDER;
95
96 public static String PROFILE_PATH;
97 public static String FRAME_PATH;
98 public static String IMAGES_PATH;
99 public static String AUDIO_PATH;
100 public static String PUBLIC_PATH;
101 public static String TRASH_PATH;
102 public static String FONT_PATH;
103 public static String DICT_PATH;
104 public static String EXPORTS_PATH;
105 public static String STATISTICS_PATH;
106 public static String LOGS_PATH;
107 public static String MESSAGES_PATH;
108 public static String MAIL_PATH;
109 public static String SHARED_FRAMESETS_PATH;
110 public static String CONTACTS_PATH;
111 public static String RESOURCES_PRIVATE_PATH;
112 public static String RESOURCES_PATH;
113 public static String FRAME_PRIVATE_PATH;
114 public static String IMAGES_PRIVATE_PATH;
115 public static String AUDIO_PRIVATE_PATH;
116 public static String HELP_PRIVATE_PATH;
117 public static String HELP_PATH;
118 public static String DEAD_DROPS_PATH;
119
120 // Paths that appear to be unused.
121 public static String TEMPLATES_PATH;
122
123 // Variables for controlling cache functionality.
124 public static final int MAX_NAME_LENGTH = 64;
125 public static final int MAX_CACHE = 100;
126 private static HashMap<String, Frame> _Cache = new FrameCache();
127 private static boolean ENABLE_CACHE = true;
128 private static boolean _UseCache = true;
129 private static boolean _SuspendedCache = false;
130
131 private static final String INF_FILENAME = "frame.inf";
132
133 public static void changeParentAndSubFolders(String newFolder) {
134 // Partial Paths.
135 PARENT_FOLDER = newFolder;
136 String resourcesPublicPath = PARENT_FOLDER + "resources-public" + File.separator;
137 String resourcesPrivateIndividualPath = PARENT_FOLDER + "resources-" + UserSettings.UserName.get() + File.separator;
138
139 // Standard paths.
140 PUBLIC_PATH = PARENT_FOLDER + "public" + File.separator;
141 TRASH_PATH = PARENT_FOLDER + "trash" + File.separator;
142 HELP_PATH = PARENT_FOLDER + "documentation" + File.separator;
143 PROFILE_PATH = PARENT_FOLDER + "profiles" + File.separator;
144 EXPORTS_PATH = PARENT_FOLDER + "exports" + File.separator;
145 STATISTICS_PATH = PARENT_FOLDER + "statistics" + File.separator;
146 LOGS_PATH = PARENT_FOLDER + "logs" + File.separator;
147
148
149 // Conditional paths
150 if (UserSettings.PublicAndPrivateResources) {
151 // Work with a system of public and private folders
152
153 FONT_PATH = resourcesPublicPath + "fonts" + File.separator;
154 DICT_PATH = resourcesPublicPath + "dict" + File.separator;
155 IMAGES_PATH = resourcesPublicPath + "images" + File.separator;
156 AUDIO_PATH = resourcesPublicPath + "audio" + File.separator;
157 FRAME_PATH = resourcesPublicPath + "framesets" + File.separator;
158 } else {
159 FONT_PATH = PARENT_FOLDER + "fonts" + File.separator;
160 DICT_PATH = PARENT_FOLDER + "dict" + File.separator;
161 IMAGES_PATH = PARENT_FOLDER + "images" + File.separator;
162 AUDIO_PATH = PARENT_FOLDER + "audio" + File.separator;
163 FRAME_PATH = PARENT_FOLDER + "framesets" + File.separator;
164 DEAD_DROPS_PATH = PARENT_FOLDER + "deaddrops" + File.separator;
165 }
166
167 if (!UserSettings.PublicAndPrivateResources || UserSettings.UserName.get().equals(Browser.USER_NOBODY)) {
168
169 if (System.getProperty("user.name").equals(Browser.USER_NOBODY)) {
170 System.err.println("**** FrameIO::changeParentAndSubFolders(): Not setting subfolders for user '"+Browser.USER_NOBODY+"'");
171 }
172
173 // If we are using the old regime, or user.name set to Browser.USER_NOBODY
174 // => then these paths should not be used.
175 RESOURCES_PATH = null;
176 SHARED_FRAMESETS_PATH = null;
177 RESOURCES_PRIVATE_PATH = null;
178 FRAME_PRIVATE_PATH = null;
179 IMAGES_PRIVATE_PATH = null;
180 AUDIO_PRIVATE_PATH = null;
181 CONTACTS_PATH = null;
182 HELP_PRIVATE_PATH = null;
183
184 if (!UserSettings.PublicAndPrivateResources) {
185 MESSAGES_PATH = PARENT_FOLDER + "messages" + File.separator;
186 } else {
187 MESSAGES_PATH = resourcesPrivateIndividualPath + "messages" + File.separator;
188 }
189
190 } else {
191 RESOURCES_PATH = resourcesPublicPath + "documentation" + File.separator;
192 SHARED_FRAMESETS_PATH = resourcesPrivateIndividualPath + "framesets-shared" + File.separator;
193
194 RESOURCES_PRIVATE_PATH = PARENT_FOLDER + "resources-private" + File.separator;
195 FRAME_PRIVATE_PATH = resourcesPrivateIndividualPath + "framesets" + File.separator;
196 IMAGES_PRIVATE_PATH = resourcesPrivateIndividualPath + "images" + File.separator;
197 AUDIO_PRIVATE_PATH = resourcesPrivateIndividualPath + "audio" + File.separator;
198 CONTACTS_PATH = resourcesPrivateIndividualPath + "contacts" + File.separator;
199 HELP_PRIVATE_PATH = resourcesPrivateIndividualPath + "documentation" + File.separator;
200 MESSAGES_PATH = resourcesPrivateIndividualPath + "messages" + File.separator;
201 MAIL_PATH = resourcesPrivateIndividualPath + "mail" + File.separator;
202 DEAD_DROPS_PATH = resourcesPrivateIndividualPath + "deaddrops" + File.separator;
203 }
204
205
206 System.err.println("**** FrameIO::changeParentAndSubFolder(): Calling AudioPathManger.changeParentAndSubFolder()");
207 AudioPathManager.changeParentAndSubFolders(newFolder);
208 }
209
210 // All methods are static, this should not be instantiated
211 private FrameIO() {
212 }
213
214 public static boolean isCacheOn() {
215 return _UseCache && ENABLE_CACHE;
216 }
217
218 public static void Precache(String framename) {
219 // if the cache is turned off, do nothing
220 if (!isCacheOn()) {
221 return;
222 }
223
224 // if the frame is already in the cache, do nothing
225 if (_Cache.containsKey(framename.toLowerCase())) {
226 return;
227 }
228
229 // otherwise, load the frame and put it in the cache
230 Logger.Log(Logger.SYSTEM, Logger.LOAD, "Precaching " + framename + ".");
231
232 // do not display errors encountered to the user
233 // (they will be shown at load time)
234 MessageBay.suppressMessages(true);
235 // loading automatically caches the frame is caching is turned on
236 LoadFromDisk(framename, null, false);
237 MessageBay.suppressMessages(false);
238 }
239
240 /**
241 * Checks if a string is a representation of a positive integer.
242 *
243 * @param s
244 * @return true if s is a positive integer
245 */
246 public static boolean isPositiveInteger(String s) {
247 if (s == null || s.length() == 0) {
248 return false;
249 }
250
251 for (int i = 0; i < s.length(); i++) {
252 if (!Character.isDigit(s.charAt(i))) {
253 return false;
254 }
255 }
256 return true;
257 }
258
259 /**
260 * Loads a frame with the specified name.
261 * By using a dot separated framename, users are able to specify the path to find the frameset in.
262 * @param frameName The frame to load.
263 * @return the loaded frame
264 */
265 public static Frame LoadFrame(String frameName) {
266 if (frameName.contains(".")) {
267 String[] split = frameName.split("\\.");
268 String[] pathSplit = Arrays.copyOfRange(split, 0, split.length - 1);
269 String name = split[split.length - 1];
270 String path = Arrays.asList(pathSplit).stream().collect(Collectors.joining(File.separator));
271 return LoadFrame(name, Paths.get(FrameIO.PARENT_FOLDER).resolve(path).toString() + File.separator, false);
272 } else {
273 return LoadFrame(frameName, null, false); //framesets-bryce.home1
274 }
275 }
276
277 public static Frame LoadFrame(String frameName, String path) {
278 return LoadFrame(frameName, path, false);
279 }
280
281 public static Frame LoadFrame(String frameName, String path, boolean ignoreAnnotations) {
282 if (!isValidFrameName(frameName)) {
283 return null;
284 }
285
286 String frameNameLower = frameName.toLowerCase();
287 // first try reading from cache
288 if (isCacheOn() && _Cache.containsKey(frameNameLower)) {
289 Logger.Log(Logger.SYSTEM, Logger.LOAD, "Loading " + frameName + " from cache.");
290 Frame frame = _Cache.get(frameNameLower);
291
292 // if frame in cache is older than the one on disk then don't use the cached one
293 File file = new File(frame.getFramePathReal());
294 long lastModified = file.lastModified();
295 if (lastModified <= frame.getLastModifyPrecise()) {
296 return frame;
297 }
298 }
299
300 Logger.Log(Logger.SYSTEM, Logger.LOAD, "Loading " + frameName
301 + " from disk.");
302
303 Frame fromDisk = LoadFromDisk(frameName, path, ignoreAnnotations);
304 return fromDisk;
305 }
306
307 //Loads the 'restore' version of a frame if there is one
308 public static Frame LoadRestoreFrame(Frame frameToRestore) {
309
310 String fullPath = getFrameFullPathName(frameToRestore.getPath(), frameToRestore
311 .getName());
312 //System.out.println("fullpath: " + fullPath);
313 String restoreVersion = fullPath + ".restore";
314 //System.out.println("restoreversion" + restoreVersion);
315 File source = new File(restoreVersion);
316 File dest = new File(fullPath);
317
318 FileChannel inputChannel = null;
319 FileChannel outputChannel = null;
320
321 try{
322 FileInputStream source_fis = new FileInputStream(source);
323 inputChannel = source_fis.getChannel();
324
325 FileOutputStream dest_fos = new FileOutputStream(dest);
326 outputChannel = dest_fos.getChannel();
327
328 outputChannel.transferFrom(inputChannel, 0, inputChannel.size());
329 inputChannel.close();
330 outputChannel.close();
331 source_fis.close();
332 dest_fos.close();
333 }
334 catch(Exception e){
335 System.err.println("No restore point detected.");
336 }
337 String frameName = frameToRestore.getName();
338 String frameNameLower = frameName.toLowerCase();
339
340 // first try reading from cache
341 if (isCacheOn() && _Cache.containsKey(frameNameLower)) {
342 Logger.Log(Logger.SYSTEM, Logger.LOAD, "Clearing " + frameName
343 + " from cache.");
344 _Cache.remove(frameNameLower);
345 }
346
347 return LoadFrame(frameName, frameToRestore.getPath(), true);
348 }
349
350 public static BufferedReader LoadPublicFrame(String frameName) {
351 String fullPath = FrameIO.getFrameFullPathName(PUBLIC_PATH, frameName);
352
353 if (fullPath == null) {
354 return null;
355 }
356
357 File frameFile = new File(fullPath);
358 if (frameFile.exists() && frameFile.canRead()) {
359 try {
360 return new BufferedReader(new FileReader(frameFile));
361 } catch (FileNotFoundException e) {
362 e.printStackTrace();
363 }
364 }
365 return null;
366 }
367
368 private static Frame LoadFromDisk(String framename, String knownPath,
369 boolean ignoreAnnotations) {
370 Frame loaded = null;
371
372 if (knownPath != null) {
373 loaded = LoadKnownPath(knownPath, framename);
374 } else {
375 List<String> directoriesToSearch = FolderSettings.FrameDirs.getAbsoluteDirs();
376
377 if (Authenticator.Authenticated) {
378 // if we are running Expeditee Authenticated, consult user profile as location for framesets first
379 String profilePath = FrameIO.PROFILE_PATH + UserSettings.UserName.get() + File.separator;
380 directoriesToSearch.add(0, profilePath);
381 }
382
383 for (String path : directoriesToSearch) {
384 loaded = LoadKnownPath(path, framename);
385 if (loaded != null) {
386 break;
387 }
388 }
389 }
390
391 if (loaded == null && FrameShare.getInstance() != null) {
392 loaded = FrameShare.getInstance().loadFrame(framename, knownPath);
393 }
394
395 if (loaded != null) {
396 FrameUtils.Parse(loaded, true, ignoreAnnotations);
397 FrameIO.setSavedProperties(loaded);
398 }
399
400 return loaded;
401 }
402
403 /**
404 * Gets a list of all the framesets available to the user
405 *
406 * @return a string containing a list of all the available framesets on
407 * separate lines
408 */
409 public static String getFramesetList() {
410 StringBuffer list = new StringBuffer();
411
412 for (String path : FolderSettings.FrameDirs.getAbsoluteDirs()) {
413 File files = new File(path);
414 if (!files.exists()) {
415 continue;
416 }
417 for (File f : (new File(path)).listFiles()) {
418 if (f.isDirectory()) {
419 list.append(f.getName()).append('\n');
420 }
421 }
422 }
423 // remove the final new line char
424 list.deleteCharAt(list.length() - 1);
425 return list.toString();
426 }
427
428 /**
429 * Gets the full path and file name of the frame.
430 * This is a alias for Frame::getFramePathLogical
431 * @param path-
432 * the directory in which to look for the frameset containing the
433 * frame.
434 * @param frameName-
435 * the name of the frame for which the path is being requested.
436 * @return null if the frame can not be located.
437 */
438 public static synchronized String getFrameFullPathName(String path,
439 String frameName) {
440
441 String source;
442 String fileName = null;
443 if(frameName.contains("restore")){
444 source = path + File.separator;// + frameName;
445 fileName = path + File.separator + frameName + ExpReader.EXTENTION;
446
447 }
448 else
449 {
450 source = path + Conversion.getFramesetName(frameName)
451 + File.separator;
452 }
453
454
455 File tester = new File(source);
456 if (!tester.exists()) {
457 return null;
458 }
459
460 String fullPath;
461
462 if(frameName.contains("restore")){
463
464 fullPath = fileName;
465 }
466 else
467 {
468 // check for the new file name format
469 fullPath = source + Conversion.getFrameNumber(frameName)
470 + ExpReader.EXTENTION;
471 }
472
473 tester = new File(fullPath);
474
475 if (tester.exists()) {
476 return fullPath;
477 }
478
479 // check for oldfile name format
480 fullPath = source + Conversion.getFramesetName(frameName) + "."
481 + Conversion.getFrameNumber(frameName);
482 tester = new File(fullPath);
483
484 if (tester.exists()) {
485 return fullPath;
486 }
487
488 return null;
489 }
490
491 public static boolean canAccessFrame(String frameName) {
492 Frame current = DisplayController.getCurrentFrame();
493 // Just in case the current frame is not yet saved...
494 if (frameName.equals(current.getName())) {
495 FrameIO.SaveFrame(current, false, false);
496 current.change();
497 return true;
498 }
499
500 for (String path : FolderSettings.FrameDirs.getAbsoluteDirs()) {
501 if (getFrameFullPathName(path, frameName) != null) {
502 return true;
503 }
504 }
505 return false;
506 }
507
508 public static Collection<String> searchFrame(String frameName,
509 String pattern, String path) {
510 String fullPath = null;
511 if (path == null) {
512 for (String possiblePath : FolderSettings.FrameDirs.getAbsoluteDirs()) {
513 fullPath = getFrameFullPathName(possiblePath, frameName);
514 if (fullPath != null) {
515 break;
516 }
517 }
518 } else {
519 fullPath = getFrameFullPathName(path, frameName);
520 }
521 // If the frame was not located return null
522 if (fullPath == null) {
523 return null;
524 }
525 Collection<String> results = new LinkedList<String>();
526 // Open the file and search the text items
527 try {
528 BufferedReader reader = new BufferedReader(new FileReader(fullPath));
529 String next;
530 while (reader.ready() && ((next = reader.readLine()) != null)) {
531 if (next.startsWith("T")) {
532 String toSearch = next.substring(2);
533 if (toSearch.toLowerCase().contains(pattern)) {
534 results.add(toSearch);
535 }
536 }
537 }
538 reader.close();
539 } catch (FileNotFoundException e) {
540 e.printStackTrace();
541 return null;
542 } catch (IOException e) {
543 e.printStackTrace();
544 }
545 return results;
546 }
547
548 private static Frame LoadKnownPath(String path, String frameName) {
549 String fullPath = getFrameFullPathName(path, frameName);
550 if (fullPath == null) {
551 return null;
552 }
553
554 try {
555 FrameReader reader;
556
557 // Get the frameset name.
558 int i = frameName.length() - 1;
559 for (; i >= 0; i--) {
560 if (!Character.isDigit(frameName.charAt(i))) {
561 break;
562 }
563 }
564 if (i < 0) {
565 System.err.println("LoadKnownFrame was provided with a invalid Frame name: " + frameName);
566 return null;
567 }
568 String framesetName = frameName.substring(0, i + 1);
569
570 String redirectTo = ExpReader.redirectTo(fullPath);
571 while (redirectTo != null) {
572 fullPath = path + framesetName + File.separator + redirectTo;
573 redirectTo = ExpReader.redirectTo(fullPath);
574 }
575
576 if (fullPath.endsWith(ExpReader.EXTENTION)) {
577 if (EncryptedExpReader.isEncryptedExpediteeFile(fullPath)) {
578 reader = new EncryptedExpReader(frameName);
579 } else {
580 reader = new ExpReader(frameName);
581 }
582 } else {
583 reader = new KMSReader();
584 }
585 Frame frame = reader.readFrame(fullPath);
586
587 if (frame == null) {
588 MessageBay.errorMessage("Error: " + frameName
589 + " could not be successfully loaded.");
590 return null;
591 }
592
593 frame.setPath(path);
594
595 // do not put 0 frames or virtual frames into the cache
596 // Why are zero frames not put in the cache
597 if (_Cache.size() > MAX_CACHE) {
598 _Cache.clear();
599 }
600
601 if (frame.getNumber() > 0 && isCacheOn()) {
602 _Cache.put(frameName.toLowerCase(), frame);
603 }
604
605 return frame;
606 } catch (IOException ioe) {
607 ioe.printStackTrace();
608 Logger.Log(ioe);
609 } catch (Exception e) {
610 e.printStackTrace();
611 Logger.Log(e);
612 MessageBay.errorMessage("Error: " + frameName
613 + " could not be successfully loaded.");
614 }
615
616 return null;
617 }
618
619 public static void Reload() {
620 // disable cache
621 boolean cache = _UseCache;
622
623 _UseCache = false;
624 Frame fresh = FrameIO.LoadFrame(DisplayController.getCurrentFrame().getName());
625 _UseCache = cache;
626 if (_Cache.containsKey(fresh.getName().toLowerCase())) {
627 addToCache(fresh);
628 }
629 DisplayController.setCurrentFrame(fresh, false);
630 }
631
632 public static Frame LoadPrevious(Frame current) {
633 checkTDFC(current);
634
635 // the current name and number
636 String name = current.getFramesetName();
637 int num = current.getNumber() - 1;
638
639 // loop until a frame that exists is found
640 for (; num >= 0; num--) {
641 Frame f = LoadFrame(name + num, current.getPath());
642 if (f != null) {
643 return f;
644 }
645 }
646
647 // if we did not find another Frame then this one must be the last one
648 // in the frameset
649 MessageBay
650 .displayMessageOnce("This is the first frame in the frameset");
651 return null;
652 }
653
654 /**
655 * Returns the next Frame in the current Frameset (The Frame with the next
656 * highest Frame number) If the current Frame is the last one in the
657 * Frameset, or an error occurs then null is returned.
658 *
659 * @return The Frame after this one in the current frameset, or null
660 */
661 public static Frame LoadNext(Frame current) {
662 checkTDFC(current);
663
664 // the current name and number
665 int num = current.getNumber() + 1;
666 int max = num + 1;
667 String name = current.getFramesetName();
668
669 // read the maximum from the INF file
670 try {
671 max = ReadINF(current.getPath(), current.getFramesetName(), false);
672 } catch (IOException ioe) {
673 MessageBay.errorMessage("Error loading INF file for frameset '"
674 + name + "'");
675 return null;
676 }
677
678 // loop until a frame that exists is found
679 for (; num <= max; num++) {
680 Frame f = LoadFrame(name + num, current.getPath());
681 if (f != null) {
682 return f;
683 }
684 }
685
686 // if we did not find another Frame then this one must be the last one
687 // in the frameset
688 MessageBay.displayMessageOnce("This is the last frame in the frameset");
689 return null;
690 }
691
692 /**
693 * This method checks if the current frame has just been created with TDFC.
694 * If it has the frame is saved regardless of whether it has been edited or
695 * not and the TDFC item property is cleared. This is to ensure that the
696 * link is saved on the parent frame.
697 *
698 * @param current
699 */
700 public static void checkTDFC(Frame current) {
701 if (FrameUtils.getTdfcItem() != null) {
702 FrameUtils.setTdfcItem(null);
703 current.change();
704 }
705 }
706
707 public static Frame LoadLast(String framesetName, String path) {
708 // read the maximum from the INF file
709 int max;
710 try {
711 max = ReadINF(path, framesetName, false);
712 } catch (IOException ioe) {
713 MessageBay.errorMessage("Error loading INF file for frameset '"
714 + framesetName + "'");
715 return null;
716 }
717
718 // loop backwards until a frame that exists is found
719 for (int num = max; num > 0; num--) {
720 Frame f = LoadFromDisk(framesetName + num, path, false);
721 if (f != null) {
722 return f;
723 }
724 }
725
726 // if we did not find another Frame then this one must be the last one
727 // in the frameset
728 MessageBay.displayMessage("This is the last frame in the frameset");
729 return null;
730 }
731
732 public static Frame LoadZero(String framesetName, String path) {
733 return LoadFrame(framesetName + 0);
734 }
735
736 public static Frame LoadZero() {
737 Frame current = DisplayController.getCurrentFrame();
738 return LoadZero(current.getFramesetName(), current.getPath());
739 }
740
741 public static Frame LoadLast() {
742 Frame current = DisplayController.getCurrentFrame();
743 return LoadLast(current.getFramesetName(), current.getPath());
744 }
745
746 public static Frame LoadNext() {
747 return LoadNext(DisplayController.getCurrentFrame());
748 }
749
750 public static Frame LoadPrevious() {
751 return LoadPrevious(DisplayController.getCurrentFrame());
752 }
753
754 /**
755 * Deletes the given Frame on disk and removes the cached Frame if there is
756 * one. Also adds the deleted frame into the deletedFrames frameset.
757 *
758 * @param toDelete
759 * The Frame to be deleted
760 * @return The name the deleted frame was changed to, or null if the delete
761 * failed
762 */
763 public static String DeleteFrame(Frame toDelete) throws IOException,
764 SecurityException {
765 if (toDelete == null) {
766 return null;
767 }
768
769 // Dont delete the zero frame
770 if (toDelete.getNumber() == 0) {
771 throw new SecurityException("Deleting a zero frame is illegal");
772 }
773
774 // Dont delete the zero frame
775 if (!toDelete.isLocal()) {
776 throw new SecurityException("Attempted to delete remote frame");
777 }
778
779 SaveFrame(toDelete);
780
781 // Copy deleted frames to the DeletedFrames frameset
782 // get the last used frame in the destination frameset
783 final String DELETED_FRAMES = "DeletedFrames";
784 int lastNumber = FrameIO.getLastNumber(DELETED_FRAMES);
785 String framePath;
786 try {
787 // create the new frameset
788 Frame one = FrameIO.CreateFrameset(DELETED_FRAMES, toDelete
789 .getPath());
790 framePath = one.getPath();
791 lastNumber = 0;
792 } catch (Exception e) {
793 Frame zero = FrameIO.LoadFrame(DELETED_FRAMES + "0");
794 framePath = zero.getPath();
795 }
796
797 // get the fill path to determine which file version it is
798 String source = getFrameFullPathName(toDelete.getPath(), toDelete
799 .getName());
800
801 String oldFrameName = toDelete.getName().toLowerCase();
802 // Now save the frame in the new location
803 toDelete.setFrameset(DELETED_FRAMES);
804 toDelete.setFrameNumber(lastNumber + 1);
805 toDelete.setPath(framePath);
806 ForceSaveFrame(toDelete);
807
808 if (_Cache.containsKey(oldFrameName)) {
809 _Cache.remove(oldFrameName);
810 }
811
812 File del = new File(source);
813
814 java.io.FileInputStream ff = new java.io.FileInputStream(del);
815 ff.close();
816
817 if (del.delete()) {
818 return toDelete.getName();
819 }
820
821 return null;
822 }
823
824 /**
825 * Creates a new Frame in the given frameset and assigns it the given Title,
826 * which can be null. The newly created Frame is a copy of the frameset's .0
827 * file with the number updated based on the last recorded Frame name in the
828 * frameset's INF file.
829 *
830 * @param frameset
831 * The frameset to create the new Frame in
832 * @param frameTitle
833 * The title to assign to the newly created Frame (can be NULL).
834 * @return The newly created Frame.
835 */
836 public static synchronized Frame CreateFrame(String frameset,
837 String frameTitle, String templateFrame) throws RuntimeException {
838
839 if (!FrameIO.isValidFramesetName(frameset)) {
840 throw new RuntimeException(frameset
841 + " is not a valid frameset name");
842 }
843
844 int next = -1;
845
846 // disable caching of 0 frames
847 // Mike says: Why is caching of 0 frames being disabled?
848 /*
849 * Especially since 0 frames are not event put into the cache in the
850 * frist place
851 */
852 // SuspendCache();
853 /*
854 * Suspending the cache causes infinate loops when trying to load a zero
855 * frame which has a ao which contains an v or av which contains a link
856 * to the ao frame
857 */
858
859 String zeroFrameName = frameset + "0";
860 Frame destFramesetZero = LoadFrame(zeroFrameName);
861 if (destFramesetZero == null) {
862 throw new RuntimeException(zeroFrameName + " could not be found");
863 }
864
865 Frame template = null;
866 if (templateFrame == null) {
867 // load in frame.0
868 template = destFramesetZero;
869 } else {
870 template = LoadFrame(templateFrame);
871 if (template == null) {
872 throw new RuntimeException("LinkTemplate " + templateFrame
873 + " could not be found");
874 }
875 }
876
877 ResumeCache();
878
879 // read the next number from the INF file
880 try {
881 next = ReadINF(destFramesetZero.getPath(), frameset, true);
882 } catch (IOException ioe) {
883 ioe.printStackTrace();
884 throw new RuntimeException("INF file could not be read");
885 }
886
887 // Remove the old frame from the cache then add the new one
888 // TODO figure out some way that we can put both in the cache
889 _Cache.remove(template.getName().toLowerCase());
890 // set the number and title of the new frame
891 template.setName(frameset, ++next);
892 template.setTitle(frameTitle);
893 // _Cache.put(template.getName().toLowerCase(), template);
894
895 Logger.Log(Logger.SYSTEM, Logger.TDFC, "Creating new frame: "
896 + template.getName() + " from TDFC");
897
898 template.setOwner(UserSettings.UserName.get());
899 template.reset();
900 template.resetDateCreated();
901
902 for (Item i : template.getItems()) {
903 if (ItemUtils.startsWithTag(i, ItemUtils.TAG_PARENT)) {
904 i.setLink(null);
905 }
906 }
907
908 // do auto shrinking of the title IF not in twin frames mode and the title is not centred
909 Item titleItem = template.getTitleItem();
910
911 if (!DisplayController.isTwinFramesOn() && !Justification.center.equals(((Text)titleItem).getJustification())) {
912 if ((titleItem.getX() + 1) < template.getNameItem().getX()) {
913 int title_item_xr = titleItem.getX() + titleItem.getBoundsWidth(); // should really be '... -1'
914 int frame_name_xl = template.getNameItem().getX();
915 if (frame_name_xl < DisplayController.MINIMUM_FRAME_WIDTH) {
916 frame_name_xl = DisplayController.MINIMUM_FRAME_WIDTH;
917 }
918
919 while ((titleItem.getSize() > Text.MINIMUM_FONT_SIZE) && title_item_xr > frame_name_xl) {
920 titleItem.setSize(titleItem.getSize() - 1);
921 System.err.println("**** shrunk titleItem: " + titleItem + " to font size: " + titleItem.getSize());
922 }
923 } else {
924 System.out.println("Bad title x position: " + titleItem.getX());
925 }
926 }
927 // Assign a width to the title.
928 titleItem.setRightMargin(template.getNameItem().getX(), true);
929
930 return template;
931 }
932
933 public static void DisableCache() {
934 _UseCache = false;
935 }
936
937 public static void EnableCache() {
938 _UseCache = true;
939 }
940
941 public static void SuspendCache() {
942 if (_UseCache) {
943 DisableCache();
944 _SuspendedCache = true;
945 } else {
946 _SuspendedCache = false;
947 }
948 }
949
950 public static void ResumeCache() {
951 if (_SuspendedCache) {
952 EnableCache();
953 _SuspendedCache = false;
954 }
955 }
956
957 public static void RefreshCacheImages()
958 {
959 SuspendCache();
960 for (Frame frame : _Cache.values()) {
961 frame.setBuffer(null);
962 }
963 ResumeCache();
964 }
965
966 /**
967 * Creates a new frameset using the given name. This includes creating a new
968 * subdirectory in the <code>FRAME_PATH</code> directory, Copying over the
969 * default.0 frame from the default frameset, copying the .0 Frame to make a
970 * .1 Frame, and creating the frameset's INF file.
971 *
972 * @param frameset
973 * The name of the Frameset to create
974 * @return The first Frame of the new Frameset (Frame.1)
975 */
976 public static Frame CreateFrameset(String frameset, String path)
977 throws Exception {
978 return CreateFrameset(frameset, path, false);
979 }
980
981 /**
982 * Tests if the given String is a 'proper' framename, that is, the String
983 * must begin with a character, end with a number with 0 or more letters and
984 * numbers in between.
985 *
986 * @param frameName
987 * The String to test for validity as a frame name
988 * @return True if the given framename is proper, false otherwise.
989 */
990 public static boolean isValidFrameName(String frameName) {
991
992 if (frameName == null || frameName.length() < 2) {
993 return false;
994 }
995
996 int lastCharIndex = frameName.length() - 1;
997 // String must begin with a letter and end with a digit
998 if (!Character.isLetter(frameName.charAt(0))
999 || !Character.isDigit(frameName.charAt(lastCharIndex))) {
1000 return false;
1001 }
1002
1003 // All the characters between first and last must be letters
1004 // or digits
1005 for (int i = 1; i < lastCharIndex; i++) {
1006 if (!isValidFrameNameChar(frameName.charAt(i))) {
1007 return false;
1008 }
1009 }
1010 return true;
1011 }
1012
1013 private static boolean isValidFrameNameChar(char c) {
1014 return c == '-' || c == '.' || Character.isLetterOrDigit(c);
1015 }
1016
1017 /**
1018 * Saves the given Frame to disk in the corresponding frameset directory.
1019 * This is the same as calling SaveFrame(toSave, true)
1020 *
1021 * @param toSave
1022 * The Frame to save to disk
1023 */
1024 public static String SaveFrame(Frame toSave) {
1025 return SaveFrame(toSave, true);
1026 }
1027
1028 /**
1029 * Saves a frame.
1030 *
1031 * @param toSave
1032 * the frame to save
1033 * @param inc
1034 * true if the frames counter should be incremented
1035 * @return the text content of the frame
1036 */
1037 public static String SaveFrame(Frame toSave, boolean inc) {
1038 return SaveFrame(toSave, inc, true);
1039 }
1040
1041 /**
1042 * Saves the given Frame to disk in the corresponding frameset directory, if
1043 * inc is true then the saved frames counter is incremented, otherwise it is
1044 * untouched.
1045 *
1046 * @param toSave
1047 * The Frame to save to disk
1048 * @param inc
1049 * True if the saved frames counter should be incremented, false otherwise.
1050 * @param checkBackup
1051 * True if the frame should be checked for the back up tag
1052 */
1053 public static String SaveFrame(Frame toSave, boolean inc, boolean checkBackup) {
1054 // TODO When loading a frame maybe append onto the event history too-
1055 // with a break to indicate the end of a session
1056
1057 if (toSave == null || !toSave.hasChanged() || toSave.isSaved()) {
1058 return "";
1059 }
1060
1061 // Dont save if the frame is protected and it exists
1062 if (checkBackup && toSave.isReadOnly()) {
1063 _Cache.remove(toSave.getName().toLowerCase());
1064 return "";
1065 }
1066
1067 /* Dont save the frame if it has the noSave tag */
1068 if (toSave.hasAnnotation("nosave")) {
1069 Actions.LegacyPerformActionCatchErrors(toSave, null, "Restore");
1070 return "";
1071 }
1072
1073 // Save frame that is not local through the Networking classes
1074 if (!toSave.isLocal()) {
1075 return FrameShare.getInstance().saveFrame(toSave);
1076 }
1077
1078 /* Format the frame if it has the autoFormat tag */
1079 if (toSave.hasAnnotation("autoformat")) {
1080 Actions.LegacyPerformActionCatchErrors(toSave, null, "Format");
1081 }
1082
1083 /**
1084 * Get the full path only to determine which format to use for saving
1085 * the frame. At this stage use Exp format for saving Exp frames only.
1086 * Later this will be changed so that KMS frames will be updated to the
1087 * Exp format.
1088 */
1089 String fullPath = getFrameFullPathName(toSave.getPath(), toSave
1090 .getName());
1091
1092 // Check if the frame exists
1093 if (checkBackup && fullPath == null) {
1094 // The first time a frame with the backup tag is saved, dont back it
1095 // up
1096 checkBackup = false;
1097 }
1098
1099 FrameWriter writer = null;
1100 int savedVersion;
1101 try {
1102 // if its a new frame or an existing Exp frame...
1103 if (fullPath == null || fullPath.endsWith(ExpReader.EXTENTION)) {
1104 if (toSave.getNumber() != Authenticator.CREDENTIALS_FRAME &&
1105 toSave.getEncryptionLabel() != null) {
1106 writer = new EncryptedExpWriter(toSave.getEncryptionLabel());
1107 savedVersion = EncryptedExpReader.getVersion(fullPath);
1108 } else {
1109 writer = new ExpWriter();
1110 savedVersion = ExpReader.getVersion(fullPath);
1111 }
1112
1113 // Is the file this would be saved to a redirect?
1114 String redirectTo = ExpReader.redirectTo(fullPath);
1115 if (redirectTo != null) {
1116 String redirectedPath = toSave.getFramePathReal();
1117 writer.setOutputLocation(redirectedPath);
1118 }
1119
1120 } else {
1121 writer = new KMSWriter();
1122 savedVersion = KMSReader.getVersion(fullPath);
1123 }
1124
1125 // Check if the frame doesnt exist
1126 // if (savedVersion < 0) {
1127 // /*
1128 // * This will happen if the user has two Expeditee's running at
1129 // * once and closes the first. When the second one closes the
1130 // * messages directory will have been deleted.
1131 // */
1132 // MessageBay
1133 // .errorMessage("Could not save frame that does not exist: "
1134 // + toSave.getName());
1135 // return null;
1136 // }
1137
1138 // Check if we are trying to save an out of date version
1139 String framesetName = toSave.getFramesetName();
1140 boolean isBayFrameset =
1141 framesetName.equalsIgnoreCase(MessageBay.MESSAGES_FRAMESET_NAME) ||
1142 framesetName.equalsIgnoreCase(MailBay.EXPEDITEE_MAIL_FRAMESET_NAME);
1143 long frameLastModify = toSave.getLastModifyPrecise();
1144 long fileLastModify = new File(toSave.getFramePathReal()).lastModified();
1145 boolean fileModifyConflict = fileLastModify > frameLastModify && !isBayFrameset;
1146 boolean versionConflict = savedVersion > toSave.getVersion() && !isBayFrameset;
1147 if (fileModifyConflict || versionConflict) {
1148 // remove this frame from the cache if it is there
1149 // This will make sure links to the original are set correctly
1150 _Cache.remove(toSave.getName().toLowerCase());
1151 int nextnum = ReadINF(toSave.getPath(), toSave
1152 .getFramesetName(), false) + 1;
1153 SuspendCache();
1154 Frame original = LoadFrame(toSave.getName());
1155 toSave.setFrameNumber(nextnum);
1156 ResumeCache();
1157 // Put the modified version in the cache
1158 addToCache(toSave);
1159 // Show the messages alerting the user
1160 Text originalMessage = new Text(-1);
1161 originalMessage.setColor(MessageBay.ERROR_COLOR);
1162 StringBuilder message = new StringBuilder(original.getName()
1163 + " was updated by another user. ");
1164 if (fileModifyConflict) {
1165 message.append("{ File modify conflict }");
1166 }
1167 if (versionConflict) {
1168 message.append("{ Version conflict }");
1169 }
1170 originalMessage.setText(message.toString());
1171 originalMessage.setLink(original.getName());
1172 Text yourMessage = new Text(-1);
1173 yourMessage.setColor(MessageBay.ERROR_COLOR);
1174 yourMessage.setText("Your version was renamed "
1175 + toSave.getName());
1176 yourMessage.setLink(toSave.getName());
1177 MessageBay.displayMessage(originalMessage);
1178 MessageBay.displayMessage(yourMessage);
1179 EcosystemManager.getMiscManager().beep();
1180 } else if (checkBackup
1181 && ItemUtils.ContainsExactTag(toSave.getItems(),
1182 ItemUtils.TAG_BACKUP)) {
1183 SuspendCache();
1184 String oldFramesetName = toSave.getFramesetName() + "-old";
1185
1186 Frame original = LoadFrame(toSave.getName());
1187 if (original == null) {
1188 original = toSave;
1189 }
1190 int orignum = original.getNumber();
1191
1192 int nextnum = -1;
1193 try {
1194 nextnum = ReadINF(toSave.getPath(), oldFramesetName, false) + 1;
1195 } catch (RuntimeException e) {
1196 try {
1197 CreateFrameset(oldFramesetName, toSave.getPath());
1198 nextnum = 1;
1199 } catch (Exception e1) {
1200 e1.printStackTrace();
1201 }
1202 // e.printStackTrace();
1203 }
1204
1205 if (nextnum > 0) {
1206 original.setFrameset(oldFramesetName);
1207 original.setFrameNumber(nextnum);
1208 original.setPermission(new PermissionPair(UserAppliedPermission.copy));
1209 original.change();
1210 SaveFrame(original, false, false);
1211 }
1212
1213 Item i = ItemUtils.FindExactTag(toSave.getItems(),
1214 ItemUtils.TAG_BACKUP);
1215 i.setLink(original.getName());
1216 toSave.setFrameNumber(orignum);
1217 ResumeCache();
1218 }
1219
1220 // int oldMode = FrameGraphics.getMode();
1221 // if (oldMode != FrameGraphics.MODE_XRAY)
1222 // FrameGraphics.setMode(FrameGraphics.MODE_XRAY, true);
1223
1224 writer.writeFrame(toSave);
1225 // FrameGraphics.setMode(oldMode, true);
1226 toSave.setSaved();
1227
1228 // Update general stuff about frame
1229 setSavedProperties(toSave);
1230
1231 if (inc) {
1232 SessionStats.SavedFrame(toSave.getName());
1233 }
1234
1235 // avoid out-of-sync frames (when in TwinFrames mode)
1236 if (_Cache.containsKey(toSave.getName().toLowerCase())) {
1237 addToCache(toSave);
1238 }
1239
1240 Logger.Log(Logger.SYSTEM, Logger.SAVE, "Saving " + toSave.getName()
1241 + " to disk.");
1242
1243 // check that the INF file is not out of date
1244 int last = ReadINF(toSave.getPath(), toSave.getFramesetName(),
1245 false);
1246 if (last <= toSave.getNumber()) {
1247 WriteINF(toSave.getPath(), toSave.getFramesetName(), toSave
1248 .getName());
1249 }
1250
1251 // check if this was the profile frame (and thus needs
1252 // re-parsing)
1253 if (isProfileFrame(toSave)) {
1254 Frame profile = FrameIO.LoadFrame(toSave.getFramesetName() + "1");
1255 assert (profile != null);
1256 FrameUtils.ParseProfile(profile);
1257 }
1258 } catch (IOException ioe) {
1259 ioe.printStackTrace();
1260 ioe.getStackTrace();
1261 Logger.Log(ioe);
1262 return null;
1263 }
1264 toSave.notifyObservers(false);
1265
1266 return writer.getFileContents();
1267 }
1268
1269 /**
1270 * Saves the given Frame to disk in the corresponding frameset directory as a RESTORE, if
1271 * inc is true then the saved frames counter is incremented, otherwise it is
1272 * untouched.
1273 *
1274 * @param toSave
1275 * The Frame to save to disk as the DEFAULT COPY
1276 * @param inc
1277 * True if the saved frames counter should be incremented, false
1278 * otherwise.
1279 * @param checkBackup
1280 * True if the frame should be checked for the back up tag
1281 */
1282 public static String SaveFrameAsRestore(Frame toSave, boolean inc,
1283 boolean checkBackup) {
1284
1285 String sf = SaveFrame(toSave, inc, checkBackup);
1286 String fullPath = getFrameFullPathName(toSave.getPath(), toSave
1287 .getName());
1288 //System.out.println(fullPath);
1289 String restoreVersion = fullPath + ".restore";
1290 File source = new File(fullPath);
1291 File dest = new File(restoreVersion);
1292
1293 FileChannel inputChannel = null;
1294 FileChannel outputChannel = null;
1295
1296 try{
1297 FileInputStream source_fis = new FileInputStream(source);
1298 inputChannel = source_fis.getChannel();
1299
1300 FileOutputStream dest_fos = new FileOutputStream(dest);
1301 outputChannel = dest_fos.getChannel();
1302
1303 outputChannel.transferFrom(inputChannel, 0, inputChannel.size());
1304 inputChannel.close();
1305 outputChannel.close();
1306 source_fis.close();
1307 dest_fos.close();
1308 }
1309 catch(Exception e){
1310 e.printStackTrace();
1311 }
1312
1313 return sf;
1314 }
1315
1316 /**
1317 * @param toAdd
1318 */
1319 public static void addToCache(Frame toAdd) {
1320 _Cache.put(toAdd.getName().toLowerCase(), toAdd);
1321 }
1322
1323 public static void ClearCache() {
1324 _Cache.clear();
1325 }
1326
1327 /**
1328 * Checks if a frame is in the current user profile frameset.
1329 *
1330 * @param toCheck
1331 * the frame to check
1332 * @return true if the frame is in the current user profile frameset
1333 */
1334 public static boolean isProfileFrame(Frame toCheck)
1335 {
1336 if (toCheck.getNumber() == 0) {
1337 return false;
1338 }
1339
1340 return toCheck.getPath().equals(PROFILE_PATH);
1341 // return toCheck.getFramesetName()
1342 // .equalsIgnoreCase(UserSettings.ProfileName);
1343 }
1344
1345 public static Frame LoadProfile(String userName)
1346 {
1347 final String profilesLoc = System.getProperty("profiles.loc");
1348 if (profilesLoc != null) {
1349 return LoadFrame(userName + "1", profilesLoc);
1350 } else {
1351 return LoadFrame(userName + "1");
1352 }
1353 }
1354
1355 public static Frame CreateNewProfile(String username, Map<String, Setting> initialSettings, Map<String, Consumer<Frame>> toNotifyOnSet) throws InvalidFramesetNameException, ExistingFramesetException {
1356 Frame profile = CreateFrameset(username, PROFILE_PATH, true);
1357 FrameUtils.CreateDefaultProfile(username, profile, initialSettings, toNotifyOnSet);
1358 return profile;
1359 }
1360
1361 /**
1362 * Reads the INF file that corresponds to the given Frame name
1363 *
1364 * @param framename
1365 * The Frame to lookup the INF file for
1366 * @throws IOException
1367 * Any exceptions encountered by the BufferedReader used to read
1368 * the INF.
1369 */
1370 public static int ReadINF(String path, String frameset, boolean update)
1371 throws IOException {
1372 assert (!frameset.endsWith("."));
1373 try {
1374 // read INF
1375 BufferedReader reader;
1376 try {
1377 // Check on the local drive
1378 reader = new BufferedReader(new FileReader(path
1379 + frameset.toLowerCase() + File.separator
1380 + INF_FILENAME));
1381 } catch (Exception e) {
1382 reader = new BufferedReader(new FileReader(path
1383 + frameset.toLowerCase() + File.separator
1384 + frameset.toLowerCase() + ".inf"));
1385 }
1386 String inf = reader.readLine();
1387 reader.close();
1388
1389 int next = Conversion.getFrameNumber(inf);
1390 // update INF file
1391 if (update) {
1392 try {
1393 WriteINF(path, frameset, frameset + (next + 1));
1394 } catch (IOException ioe) {
1395 ioe.printStackTrace();
1396 Logger.Log(ioe);
1397 }
1398 }
1399 return next;
1400 } catch (Exception e) {
1401 }
1402
1403 // Check peers
1404 return FrameShare.getInstance().getInfNumber(path, frameset, update);
1405 }
1406
1407 /**
1408 * Writes the given String out to the INF file corresponding to the current
1409 * frameset.
1410 *
1411 * @param toWrite
1412 * The String to write to the file.
1413 * @throws IOException
1414 * Any exception encountered by the BufferedWriter.
1415 */
1416 public static void WriteINF(String path, String frameset, String frameName)
1417 throws IOException {
1418 try {
1419 assert (!frameset.endsWith("."));
1420
1421 path += frameset.toLowerCase() + File.separator + INF_FILENAME;
1422
1423 BufferedWriter writer = new BufferedWriter(new FileWriter(path));
1424 writer.write(frameName);
1425 writer.close();
1426 } catch (Exception e) {
1427
1428 }
1429 }
1430
1431 public static boolean FrameIsCached(String name) {
1432 return _Cache.containsKey(name);
1433 }
1434
1435 /**
1436 * Gets a frame from the cache.
1437 *
1438 * @param name
1439 * The frame to get from the cache
1440 *
1441 * @return The frame from cache. Null if not cached.
1442 */
1443 public static Frame FrameFromCache(String name) {
1444 return _Cache.get(name);
1445 }
1446
1447 public static String ConvertToValidFramesetName(String toValidate) {
1448 assert (toValidate != null && toValidate.length() > 0);
1449
1450 StringBuffer result = new StringBuffer();
1451
1452 if (Character.isDigit(toValidate.charAt(0))) {
1453 result.append(FRAME_NAME_LAST_CHAR);
1454 }
1455
1456 boolean capital = false;
1457 for (int i = 0; i < toValidate.length()
1458 && result.length() < MAX_NAME_LENGTH; i++) {
1459 char cur = toValidate.charAt(i);
1460
1461 // capitalize all characters after spaces
1462 if (Character.isLetterOrDigit(cur)) {
1463 if (capital) {
1464 capital = false;
1465 result.append(Character.toUpperCase(cur));
1466 } else {
1467 result.append(cur);
1468 }
1469 } else {
1470 capital = true;
1471 }
1472 }
1473 assert (result.length() > 0);
1474 int lastCharIndex = result.length() - 1;
1475 if (!Character.isLetter(result.charAt(lastCharIndex))) {
1476 if (lastCharIndex == MAX_NAME_LENGTH - 1) {
1477 result.setCharAt(lastCharIndex, FRAME_NAME_LAST_CHAR);
1478 } else {
1479 result.append(FRAME_NAME_LAST_CHAR);
1480 }
1481 }
1482
1483 assert (isValidFramesetName(result.toString()));
1484 return result.toString();
1485 }
1486
1487 public static Frame CreateNewFrame(Item linker) throws RuntimeException {
1488 String title = linker.getName();
1489
1490 String templateLink = linker.getAbsoluteLinkTemplate();
1491 String framesetLink = linker.getAbsoluteLinkFrameset();
1492 String frameset = (framesetLink != null ? framesetLink : DisplayController
1493 .getCurrentFrame().getFramesetName());
1494
1495 Frame newFrame = FrameIO.CreateFrame(frameset, title, templateLink);
1496 return newFrame;
1497 }
1498
1499 public static Frame CreateNewFrame(Item linker, OnNewFrameAction action) throws RuntimeException {
1500 Frame newFrame = FrameIO.CreateNewFrame(linker);
1501 if(action != null) {
1502 action.exec(linker, newFrame);
1503 }
1504 return newFrame;
1505 }
1506
1507 /**
1508 * Creates a new Frameset on disk, including a .0, .1, and .inf files. The
1509 * Default.0 frame is copied to make the initial .0 and .1 Frames
1510 *
1511 * @param name
1512 * The Frameset name to use
1513 * @return The name of the first Frame in the newly created Frameset (the .1
1514 * frame)
1515 */
1516 public static Frame CreateNewFrameset(String name) throws Exception {
1517 String path = DisplayController.getCurrentFrame().getPath();
1518
1519 // if current frameset is profile directory change it to framesets
1520 if (path.equals(FrameIO.PROFILE_PATH)) {
1521 path = FrameIO.FRAME_PATH;
1522 }
1523
1524 Frame newFrame = FrameIO.CreateFrameset(name, path);
1525
1526 if (newFrame == null) {
1527 // Cant create directories if the path is readonly or there is no
1528 // space available
1529 newFrame = FrameIO.CreateFrameset(name, FrameIO.FRAME_PATH);
1530 }
1531
1532 if (newFrame == null) {
1533 // TODO handle running out of disk space here
1534 }
1535
1536 return newFrame;
1537 }
1538
1539 /**
1540 *
1541 * @param frameset
1542 * @return
1543 */
1544 public static int getLastNumber(String frameset) { // Rob thinks it might
1545 // have been
1546 // GetHighestNumExFrame
1547 // TODO minimise the number of frames being read in!!
1548 int num = -1;
1549
1550 Frame zero = LoadFrame(frameset + "0");
1551
1552 // the frameset does not exist (or has no 0 frame)
1553 if (zero == null) {
1554 return -1;
1555 }
1556
1557 try {
1558 num = ReadINF(zero.getPath(), frameset, false);
1559 } catch (IOException e) {
1560 // TODO Auto-generated catch block
1561 // e.printStackTrace();
1562 }
1563
1564 /*
1565 * Michael doesnt think the code below is really needed... it will just
1566 * slow things down when we are reading frames over a network***** for (;
1567 * num >= 0; num--) { System.out.println("This code is loading frames to
1568 * find the highest existing frame..."); if (LoadFrame(frameset + num) !=
1569 * null) break; }
1570 */
1571
1572 return num;
1573 }
1574
1575 /**
1576 * Checks if a given frameset is accessable.
1577 *
1578 * @param framesetName
1579 * @return
1580 */
1581 public static Boolean canAccessFrameset(String framesetName) {
1582 framesetName = framesetName.toLowerCase();
1583 for (String path : FolderSettings.FrameDirs.getAbsoluteDirs()) {
1584 if ((new File(path + framesetName)).exists()) {
1585 return true;
1586 }
1587 }
1588 return false;
1589 }
1590
1591 public static Frame CreateFrameset(String frameset, String path, boolean recreate) throws InvalidFramesetNameException, ExistingFramesetException {
1592 String conversion = frameset + " --> ";
1593
1594 if (!isValidFramesetName(frameset)) {
1595 throw new InvalidFramesetNameException(frameset);
1596 }
1597
1598 if (!recreate && FrameIO.canAccessFrameset(frameset)) {
1599 throw new ExistingFramesetException(frameset);
1600 }
1601
1602 conversion += frameset;
1603 Logger.Log(Logger.SYSTEM, Logger.NEW_FRAMESET, "Frameset Name: "
1604 + conversion);
1605 conversion = frameset;
1606
1607 /**
1608 * TODO: Update this to exclude any\all invalid filename characters
1609 */
1610 // ignore annotation character
1611 if (frameset.startsWith("@")) {
1612 frameset = frameset.substring(1);
1613 }
1614
1615 conversion += " --> " + frameset;
1616 Logger.Log(Logger.SYSTEM, Logger.NEW_FRAMESET, "Name: " + conversion);
1617
1618 // create the new Frameset directory
1619 File dir = new File(path + frameset.toLowerCase() + File.separator);
1620
1621 // If the directory doesnt already exist then create it...
1622 if (!dir.exists()) {
1623 // If the directory couldnt be created, then there is something
1624 // wrong... ie. The disk is full.
1625 if (!dir.mkdirs()) {
1626 return null;
1627 }
1628 }
1629
1630 // create the new INF file
1631 try {
1632 WriteINF(path, frameset, frameset + '1');
1633 } catch (IOException ioe) {
1634 ioe.printStackTrace();
1635 Logger.Log(ioe);
1636 }
1637
1638 SuspendCache();
1639 // copy the default .0 and .1 files
1640 Frame base = null;
1641 try {
1642 base = LoadFrame(TemplateSettings.DefaultFrame.get());
1643 } catch (Exception e) {
1644 }
1645 // The frame may not be accessed for various reasons... in all these
1646 // cases just create a new one
1647 if (base == null) {
1648 base = new Frame();
1649 }
1650
1651 ResumeCache();
1652
1653 base.reset();
1654 base.resetDateCreated();
1655 base.setFrameset(frameset);
1656 base.setFrameNumber(0);
1657 base.setTitle(base.getFramesetName() + "0");
1658 base.setPath(path);
1659 base.change();
1660 base.setOwner(UserSettings.UserName.get());
1661 SaveFrame(base, false);
1662
1663 base.reset();
1664 base.resetDateCreated();
1665 base.setFrameNumber(1);
1666 base.setTitle(frameset);
1667 base.change();
1668 base.setOwner(UserSettings.UserName.get());
1669 SaveFrame(base, true);
1670
1671 FrameIO.setSavedProperties(base);
1672
1673 Logger.Log(Logger.SYSTEM, Logger.NEW_FRAMESET, "Created new frameset: " + frameset);
1674
1675 return base;
1676 }
1677
1678 /**
1679 * Tests if a frameset name is valid. That is it must begin and end with a
1680 * letter and contain only letters and digits in between.
1681 *
1682 * @param frameset
1683 * the name to be tested
1684 * @return true if the frameset name is valid
1685 */
1686 public static boolean isValidFramesetName(String frameset) {
1687 if (frameset == null) {
1688 return false;
1689 }
1690
1691 int nameLength = frameset.length();
1692 if (frameset.length() <= 0 || nameLength > MAX_NAME_LENGTH) {
1693 return false;
1694 }
1695
1696 int lastCharIndex = nameLength - 1;
1697
1698 if (!Character.isLetter(frameset.charAt(0))
1699 || !Character.isLetter(frameset.charAt(lastCharIndex))) {
1700 return false;
1701 }
1702
1703 for (int i = 1; i < lastCharIndex; i++) {
1704 if (!isValidFrameNameChar(frameset.charAt(i))) {
1705 return false;
1706 }
1707 }
1708 return true;
1709 }
1710
1711 public static boolean deleteFrameset(String framesetName) {
1712 return moveFrameset(framesetName, FrameIO.TRASH_PATH);
1713 }
1714
1715 public static boolean moveFrameset(String framesetName,
1716 String destinationFolder) {
1717 if (!FrameIO.canAccessFrameset(framesetName)) {
1718 return false;
1719 }
1720 // Clear the cache
1721 _Cache.clear();
1722
1723 // Search all the available directories for the directory
1724 for (String path : FolderSettings.FrameDirs.getAbsoluteDirs()) {
1725 String source = path + framesetName.toLowerCase() + File.separator;
1726 File framesetDirectory = new File(source);
1727 // Once we have found the directory move it
1728 if (framesetDirectory.exists()) {
1729 String destPath = destinationFolder
1730 + framesetName.toLowerCase();
1731 int copyNumber = 1;
1732 File dest = new File(destPath + File.separator);
1733 // Create the destination folder if it doesnt already exist
1734 if (!dest.getParentFile().exists()) {
1735 dest.mkdirs();
1736 }
1737 // If a frameset with the same name is already in the
1738 // destination add
1739 // a number to the end
1740 while (dest.exists()) {
1741 dest = new File(destPath + ++copyNumber + File.separator);
1742 }
1743 if (!framesetDirectory.renameTo(dest)) {
1744 for (File f : framesetDirectory.listFiles()) {
1745 if (!f.delete()) {
1746 return false;
1747 }
1748 }
1749 if (!framesetDirectory.delete()) {
1750 return false;
1751 }
1752 }
1753 return true;
1754 }
1755 }
1756 return false;
1757 }
1758
1759 public static boolean CopyFrameset(String framesetToCopy,
1760 String copiedFrameset) throws Exception {
1761 if (!FrameIO.canAccessFrameset(framesetToCopy)) {
1762 return false;
1763 }
1764 if (FrameIO.canAccessFrameset(copiedFrameset)) {
1765 return false;
1766 }
1767 // search through all the directories to find the frameset we are
1768 // copying
1769 for (String path : FolderSettings.FrameDirs.getAbsoluteDirs()) {
1770 String source = path + framesetToCopy.toLowerCase()
1771 + File.separator;
1772 File framesetDirectory = new File(source);
1773 if (framesetDirectory.exists()) {
1774 // copy the frameset
1775 File copyFramesetDirectory = new File(path
1776 + copiedFrameset.toLowerCase() + File.separator);
1777 if (!copyFramesetDirectory.mkdirs()) {
1778 return false;
1779 }
1780 // copy each of the frames
1781 for (File f : framesetDirectory.listFiles()) {
1782 // Ignore hidden files
1783 if (f.getName().charAt(0) == '.') {
1784 continue;
1785 }
1786 String copyPath = copyFramesetDirectory.getAbsolutePath()
1787 + File.separator + f.getName();
1788 FrameIO.copyFile(f.getAbsolutePath(), copyPath);
1789 }
1790 return true;
1791 }
1792 }
1793 return false;
1794 }
1795
1796 /**
1797 * Copies a file from one location to another.
1798 *
1799 * @param existingFile
1800 * @param newFileName
1801 * @throws Exception
1802 */
1803 public static void copyFile(String existingFile, String newFileName)
1804 throws IOException {
1805 FileInputStream is = new FileInputStream(existingFile);
1806 FileOutputStream os = new FileOutputStream(newFileName, false);
1807 int data;
1808 while ((data = is.read()) != -1) {
1809 os.write(data);
1810 }
1811 os.flush();
1812 os.close();
1813 is.close();
1814 }
1815
1816 /**
1817 * Saves a frame regardless of whether or not the frame is marked as having
1818 * been changed.
1819 *
1820 * @param frame
1821 * the frame to save
1822 * @return the contents of the frame or null if it could not be saved
1823 */
1824 public static String ForceSaveFrame(Frame frame) {
1825 frame.change();
1826 return SaveFrame(frame, false);
1827 }
1828
1829 public static boolean isValidLink(String frameName) {
1830 return frameName == null || isPositiveInteger(frameName)
1831 || isValidFrameName(frameName);
1832 }
1833
1834 public static void SavePublicFrame(String peerName, String frameName,
1835 int version, BufferedReader packetContents) {
1836 // TODO handle versioning - add version to the header
1837 // Remote user uploads version based on an old version
1838
1839 // Remove it from the cache so that next time it is loaded we get the up
1840 // todate version
1841 _Cache.remove(frameName.toLowerCase());
1842
1843 // Save to file
1844 String filename = PUBLIC_PATH + Conversion.getFramesetName(frameName)
1845 + File.separator + Conversion.getFrameNumber(frameName)
1846 + ExpReader.EXTENTION;
1847
1848 File file = new File(filename);
1849 // Ensure the file exists
1850 if (file.exists()) {
1851 // Check the versions
1852 int savedVersion = ExpReader.getVersion(filename);
1853
1854 if (savedVersion > version) {
1855 // remove this frame from the cache if it is there
1856 // This will make sure links to the original are set correctly
1857 // _Cache.remove(frameName.toLowerCase());
1858
1859 int nextNum = 0;
1860 try {
1861 nextNum = ReadINF(PUBLIC_PATH, Conversion
1862 .getFramesetName(frameName), false) + 1;
1863 } catch (IOException e) {
1864 e.printStackTrace();
1865 }
1866
1867 String newName = Conversion.getFramesetName(frameName)
1868 + nextNum;
1869 filename = PUBLIC_PATH + Conversion.getFramesetName(frameName)
1870 + File.separator + nextNum + ExpReader.EXTENTION;
1871
1872 // Show the messages alerting the user
1873 Text originalMessage = new Text(-1);
1874 originalMessage.setColor(MessageBay.ERROR_COLOR);
1875 originalMessage.setText(frameName + " was edited by "
1876 + peerName);
1877 originalMessage.setLink(frameName);
1878 Text yourMessage = new Text(-1);
1879 yourMessage.setColor(MessageBay.ERROR_COLOR);
1880 yourMessage.setText("Their version was renamed " + newName);
1881 yourMessage.setLink(newName);
1882 MessageBay.displayMessage(originalMessage);
1883 MessageBay.displayMessage(yourMessage);
1884
1885 Frame editedFrame = FrameIO.LoadFrame(frameName);
1886
1887 FrameShare.getInstance().sendMessage(
1888 frameName + " was recently edited by "
1889 + editedFrame.getLastModifyUser(), peerName);
1890 FrameShare.getInstance().sendMessage(
1891 "Your version was renamed " + newName, peerName);
1892 }
1893 }
1894
1895 // Save the new version
1896 try {
1897 // FileWriter fw = new FileWriter(file);
1898
1899 // Open an Output Stream Writer to set encoding
1900 OutputStream fout = new FileOutputStream(file);
1901 OutputStream bout = new BufferedOutputStream(fout);
1902 Writer fw = new OutputStreamWriter(bout, "UTF-8");
1903
1904 String nextLine = null;
1905 while ((nextLine = packetContents.readLine()) != null) {
1906 fw.write(nextLine + '\n');
1907 }
1908 fw.flush();
1909 fw.close();
1910 MessageBay.displayMessage("Saved remote frame: " + frameName);
1911 } catch (IOException e) {
1912 MessageBay.errorMessage("Error remote saving " + frameName + ": "
1913 + e.getMessage());
1914 e.printStackTrace();
1915 }
1916 }
1917
1918 public static void setSavedProperties(Frame toSave) {
1919 toSave.setLastModifyDate(Formatter.getDateTime(), System.currentTimeMillis());
1920 toSave.setLastModifyUser(UserSettings.UserName.get());
1921 toSave.setVersion(toSave.getVersion() + 1);
1922 Time darkTime = new Time(SessionStats.getFrameDarkTime().getTime()
1923 + toSave.getDarkTime().getTime());
1924 Time activeTime = new Time(SessionStats.getFrameActiveTime().getTime()
1925 + toSave.getActiveTime().getTime());
1926 toSave.setDarkTime(darkTime);
1927 toSave.setActiveTime(activeTime);
1928 }
1929
1930 public static Path setupPersonalResources(String username) {
1931 Path personalResources = Paths.get(FrameIO.PARENT_FOLDER).resolve("resources-" + username);
1932 personalResources.toFile().mkdir();
1933 File[] globalResourcesToCopy = Paths.get(FrameIO.RESOURCES_PRIVATE_PATH).toFile().listFiles();
1934
1935 try {
1936 for (File toCopy: globalResourcesToCopy) {
1937 Path p = Paths.get(toCopy.getAbsolutePath());
1938 if (!p.getFileName().toString().equals(".res") && !p.getFileName().toString().equals("about")) {
1939 copyFileTree(p.toAbsolutePath(), personalResources.resolve(p.getFileName()));
1940 }
1941 }
1942 }
1943 catch (IOException e) {
1944 e.printStackTrace();
1945 personalResources = null;
1946 }
1947
1948 return personalResources;
1949 }
1950
1951 public static void migrateFrame(Frame toMigrate, Path destinationDirectory) {
1952 Path source = Paths.get(toMigrate.getFramePathReal());
1953 String destination = source.relativize(destinationDirectory).toString().substring(3).replace(File.separator, "/");
1954 try {
1955 Files.move(source, destinationDirectory);
1956 } catch (IOException e) {
1957 System.err.println("FrameIO::migrateFrame: failed to migrate from to new location. Message: " + e.getMessage());
1958 return;
1959 }
1960 try {
1961 FileWriter out = new FileWriter(source.toFile());
1962 out.write("REDIRECT:" + destination);
1963 out.flush();
1964 out.close();
1965 } catch (IOException e) {
1966 System.err.println("FrameIO::migrateFrame: failed to update file [" + source + "] to redirect to [" + destination + "] following migration. Message: " + e.getMessage());
1967 }
1968 }
1969
1970 private static void copyFileTree(Path source, Path target) throws IOException {
1971 Files.copy(source, target);
1972 if (source.toFile().isDirectory()) {
1973 File[] files = source.toFile().listFiles();
1974 for (File file: files) {
1975 Path asPath = Paths.get(file.getAbsolutePath());
1976 copyFileTree(asPath, target.resolve(asPath.getFileName()));
1977 }
1978 }
1979 }
1980}
Note: See TracBrowser for help on using the repository browser.