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

Last change on this file since 1504 was 1504, checked in by bnemhaus, 4 years ago

Revised implementation of authenticated Expeditee mail. Motivated by bugs relating to messages not being marked as read and incorrect counting of new messages for users, the Expeditee mail system has been rewritten. The new code not only does not exhibit the previous bugs but is also better engineered. Whilst the MailBay is static (which is in line with the MessageBay), the Mail class is no longer static and must be initialised for each user as they log in.

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