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

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

Removed reliance of System.getProperty("user.name") by introducing some functions and a variable to Browser to be used instead. All previous occurrences of System.getProperty("user.name") now use these functions.

At the time, introduced new piping into various functions related to the creation and management of profile frames that distinguished between a profile name and a user name. This allows functions to be more specific about what is being used. For example, when modifying the users profile frames (in the profiles directory) that users profile name can be used instead of naming the variable 'username'. This distinction is important because while username's can end with numbers, profile names cannot and therefore get an 'A' on the end.

File size: 66.0 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(), null);
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 * @param zeroFrameItems TODO
1018 * @return The first Frame of the new Frameset (Frame.1)
1019 */
1020 public static Frame CreateFrameset(String frameset, String path, Collection<Item> zeroFrameItems)
1021 throws Exception {
1022 return CreateFrameset(frameset, path, false, zeroFrameItems);
1023 }
1024
1025 /**
1026 * Tests if the given String is a 'proper' framename, that is, the String
1027 * must begin with a character, end with a number with 0 or more letters and
1028 * numbers in between.
1029 *
1030 * @param frameName
1031 * The String to test for validity as a frame name
1032 * @return True if the given framename is proper, false otherwise.
1033 */
1034 public static boolean isValidFrameName(String frameName) {
1035
1036 if (frameName == null || frameName.length() < 2) {
1037 return false;
1038 }
1039
1040 int lastCharIndex = frameName.length() - 1;
1041 // String must begin with a letter and end with a digit
1042 if (!Character.isLetter(frameName.charAt(0))
1043 || !Character.isDigit(frameName.charAt(lastCharIndex))) {
1044 return false;
1045 }
1046
1047 // All the characters between first and last must be letters
1048 // or digits
1049 for (int i = 1; i < lastCharIndex; i++) {
1050 if (!isValidFrameNameChar(frameName.charAt(i))) {
1051 return false;
1052 }
1053 }
1054 return true;
1055 }
1056
1057 private static boolean isValidFrameNameChar(char c) {
1058 return c == '-' || c == '.' || Character.isLetterOrDigit(c);
1059 }
1060
1061 /**
1062 * Saves the given Frame to disk in the corresponding frameset directory.
1063 * This is the same as calling SaveFrame(toSave, true)
1064 *
1065 * @param toSave
1066 * The Frame to save to disk
1067 */
1068 public static String SaveFrame(Frame toSave) {
1069 return SaveFrame(toSave, true);
1070 }
1071
1072 /**
1073 * Saves a frame.
1074 *
1075 * @param toSave
1076 * the frame to save
1077 * @param inc
1078 * true if the frames counter should be incremented
1079 * @return the text content of the frame
1080 */
1081 public static String SaveFrame(Frame toSave, boolean inc) {
1082 return SaveFrame(toSave, inc, true);
1083 }
1084
1085 /**
1086 * Saves the given Frame to disk in the corresponding frameset directory, if
1087 * inc is true then the saved frames counter is incremented, otherwise it is
1088 * untouched.
1089 *
1090 * @param toSave
1091 * The Frame to save to disk
1092 * @param inc
1093 * True if the saved frames counter should be incremented, false otherwise.
1094 * @param checkBackup
1095 * True if the frame should be checked for the backup tag
1096 */
1097 public static String SaveFrame(Frame toSave, boolean inc, boolean checkBackup) {
1098 // TODO When loading a frame maybe append onto the event history too-
1099 // with a break to indicate the end of a session
1100
1101 if (toSave == null || !toSave.hasChanged() || toSave.isSaved()) {
1102 System.err.println("FrameIO::SaveFrame::Nothing to save, backing out.");
1103 return "";
1104 }
1105
1106 if (toSave.getName().toLowerCase().equals("abc146a1")) {
1107 System.out.println("here");
1108 }
1109
1110 // Don't save if the frame is protected and it exists
1111 if (checkBackup && toSave.isReadOnly()) {
1112 _Cache.remove(toSave.getName().toLowerCase());
1113 return "";
1114 }
1115
1116 /* Don't save the frame if it has the noSave tag */
1117 if (toSave.hasAnnotation("nosave")) {
1118 Actions.LegacyPerformActionCatchErrors(toSave, null, "Restore");
1119 return "";
1120 }
1121
1122 // Save frame that is not local through the Networking classes
1123 if (!toSave.isLocal()) {
1124 return FrameShare.getInstance().saveFrame(toSave);
1125 }
1126
1127 /* Format the frame if it has the autoFormat tag */
1128 if (toSave.hasAnnotation("autoformat")) {
1129 Actions.LegacyPerformActionCatchErrors(toSave, null, "Format");
1130 }
1131
1132 /**
1133 * Get the full path only to determine which format to use for saving
1134 * the frame. At this stage use Exp format for saving Exp frames only.
1135 * Later this will be changed so that KMS frames will be updated to the
1136 * Exp format.
1137 */
1138 String fullPath = getFrameFullPathName(toSave.getPath(), toSave
1139 .getName());
1140
1141 // Check if the frame exists
1142 if (checkBackup && fullPath == null) {
1143 // The first time a frame with the backup tag is saved, don't back it up
1144 checkBackup = false;
1145 }
1146
1147 FrameWriter writer = null;
1148 int savedVersion;
1149 try {
1150 // if its a new frame or an existing Exp frame...
1151 if (fullPath == null || fullPath.endsWith(ExpReader.EXTENTION)) {
1152
1153 if (toSave.isEncryptableFrame()) {
1154 String encryptionLabel = toSave.getFrameEncryptionLabel();
1155 writer = new EncryptedExpWriter(encryptionLabel);
1156 } else {
1157 writer = new ExpWriter();
1158 }
1159 savedVersion = ExpReader.getVersion(fullPath);
1160
1161 // Is the file this would be saved to a redirect?
1162 String redirectTo = ExpReader.redirectTo(fullPath);
1163 if (redirectTo != null) {
1164 String redirectedPath = toSave.getFramePathReal();
1165 writer.setOutputLocation(redirectedPath);
1166 }
1167
1168 } else {
1169 writer = new KMSWriter();
1170 savedVersion = KMSReader.getVersion(fullPath);
1171 }
1172
1173 // Check if the frame doesn't exist
1174 // if (savedVersion < 0) {
1175 // /*
1176 // * This will happen if the user has two Expeditee's running at
1177 // * once and closes the first. When the second one closes the
1178 // * messages directory will have been deleted.
1179 // */
1180 // MessageBay
1181 // .errorMessage("Could not save frame that does not exist: "
1182 // + toSave.getName());
1183 // return null;
1184 // }
1185
1186 // Check if we are trying to save an out of date version
1187 // Q: Why do we ignore version conflicts if the saved version is zero?
1188 // A: Sometimes a Frame object in memory with a specified path is not 'connected'
1189 // to the file found at that specified path yet. This occurs if a frame object
1190 // has been created, its path assigned and saved to disk; with the intention
1191 // discarding this Frame object and later saving a different Frame object to
1192 // that File. One example of this is when @old frames are created.
1193 // The new Frame object that is created and saved only to be discarded, has a
1194 // version number of zero.
1195 // Therefore, if the file created from the discarded Frame has its modification
1196 // date compared to the modification date on the Frame object that will eventually
1197 // be used to overwrite that file, it causes a false positive conflict. Checking
1198 // for the zero version number fixes this.
1199 //String framesetName = toSave.getFramesetName();
1200 boolean isBayFrameset = toSave.isBayFrameset();
1201
1202 long fileLastModify = fullPath != null ? new File(fullPath).lastModified() : 0;
1203 long frameLastModify = toSave.getLastModifyPrecise();
1204
1205 boolean fileModifyConflict = fileLastModify > frameLastModify && !isBayFrameset;
1206 boolean versionConflict = savedVersion > toSave.getVersion() && !isBayFrameset;
1207
1208 System.err.println("FrameIO::SaveFrame::Checking for conflicts for frame:" + toSave.getName());
1209
1210 if ((fileModifyConflict || versionConflict) && savedVersion > 0) {
1211 // remove this frame from the cache if it is there
1212 // This will make sure links to the original are set correctly
1213 _Cache.remove(toSave.getName().toLowerCase());
1214
1215 int nextnum = ReadINF(toSave.getPath(), toSave.getFramesetName(), false) + 1;
1216
1217 SuspendCache();
1218 Frame original = LoadFrame(toSave.getName());
1219 toSave.setFrameNumber(nextnum);
1220 ResumeCache();
1221
1222 // Put the modified version in the cache
1223 addToCache(toSave);
1224 // Show the messages alerting the user
1225 Text originalMessage = new Text(-1);
1226 originalMessage.setColor(MessageBay.ERROR_COLOR);
1227 StringBuilder message = new StringBuilder(original.getName()
1228 + " was updated by another user. ");
1229 if (fileModifyConflict) {
1230 message.append("{ File modify conflict }");
1231 System.err.println("Thread name: " + Thread.currentThread().getName());
1232 StackTraceElement[] stackTrace = Thread.currentThread().getStackTrace();
1233 for (StackTraceElement ste: stackTrace) {
1234 System.err.println(ste.toString());
1235 }
1236 }
1237 if (versionConflict) {
1238 message.append("{ Version conflict }");
1239 }
1240 originalMessage.setText(message.toString());
1241 originalMessage.setLink(original.getName());
1242 Text yourMessage = new Text(-1);
1243 yourMessage.setColor(MessageBay.ERROR_COLOR);
1244 yourMessage.setText("Your version was renamed "
1245 + toSave.getName());
1246 yourMessage.setLink(toSave.getName());
1247 MessageBay.displayMessage(originalMessage);
1248 MessageBay.displayMessage(yourMessage);
1249 EcosystemManager.getMiscManager().beep();
1250 }
1251 else if (checkBackup
1252 && ItemUtils.ContainsExactTag(toSave.getSortedItems(), ItemUtils.TAG_BACKUP)) {
1253 SuspendCache();
1254 String oldFramesetName = toSave.getFramesetName() + "-old";
1255
1256 Frame original = LoadFrame(toSave.getName());
1257 if (original == null) {
1258 original = toSave;
1259 }
1260 int orignum = original.getNumber();
1261
1262 int nextnum = -1;
1263 try {
1264 nextnum = ReadINF(toSave.getPath(), oldFramesetName, false) + 1;
1265 } catch (RuntimeException e) {
1266 try {
1267 CreateFrameset(oldFramesetName, toSave.getPath(), null);
1268 nextnum = 1;
1269 } catch (Exception e1) {
1270 e1.printStackTrace();
1271 }
1272 }
1273
1274 if (nextnum > 0) {
1275 original.setFrameset(oldFramesetName);
1276 original.setFrameNumber(nextnum);
1277 original.setPermission(new PermissionTriple(UserAppliedPermission.copy));
1278 original.change();
1279 SaveFrame(original, false, false);
1280 }
1281
1282 Item i = ItemUtils.FindExactTag(toSave.getSortedItems(),
1283 ItemUtils.TAG_BACKUP);
1284 i.setLink(original.getName());
1285 toSave.setFrameNumber(orignum);
1286 ResumeCache();
1287 }
1288
1289 // int oldMode = FrameGraphics.getMode();
1290 // if (oldMode != FrameGraphics.MODE_XRAY)
1291 // FrameGraphics.setMode(FrameGraphics.MODE_XRAY, true);
1292
1293 writer.writeFrame(toSave);
1294 // FrameGraphics.setMode(oldMode, true);
1295 toSave.setSaved();
1296
1297 // Update general stuff about frame
1298 setSavedProperties(toSave);
1299
1300 if (inc) {
1301 SessionStats.SavedFrame(toSave.getName());
1302 }
1303
1304 // avoid out-of-sync frames (when in TwinFrames mode)
1305 if (_Cache.containsKey(toSave.getName().toLowerCase())) {
1306 addToCache(toSave);
1307 }
1308
1309 Logger.Log(Logger.SYSTEM, Logger.SAVE, "Saving " + toSave.getName()
1310 + " to disk.");
1311
1312 // check that the INF file is not out of date
1313 int last = ReadINF(toSave.getPath(), toSave.getFramesetName(),
1314 false);
1315 if (last <= toSave.getNumber()) {
1316 WriteINF(toSave.getPath(), toSave.getFramesetName(), toSave
1317 .getName());
1318 }
1319
1320 // check if this was the profile frame (and thus needs
1321 // re-parsing)
1322 if (isProfileFrame(toSave)) {
1323 Frame profile = FrameIO.LoadFrame(toSave.getFramesetName() + "1");
1324 assert (profile != null);
1325 FrameUtils.ParseProfile(profile);
1326 }
1327 } catch (IOException ioe) {
1328 ioe.printStackTrace();
1329 ioe.getStackTrace();
1330 Logger.Log(ioe);
1331 return null;
1332 }
1333 toSave.notifyObservers(false);
1334
1335 return writer.getFileContents();
1336 }
1337
1338 /**
1339 * Saves the given Frame to disk in the corresponding frameset directory as a RESTORE, if
1340 * inc is true then the saved frames counter is incremented, otherwise it is
1341 * untouched.
1342 *
1343 * @param toSave
1344 * The Frame to save to disk as the DEFAULT COPY
1345 * @param inc
1346 * True if the saved frames counter should be incremented, false
1347 * otherwise.
1348 * @param checkBackup
1349 * True if the frame should be checked for the back up tag
1350 */
1351 public static String SaveFrameAsRestore(Frame toSave, boolean inc,
1352 boolean checkBackup) {
1353
1354 String sf = SaveFrame(toSave, inc, checkBackup);
1355 String fullPath = getFrameFullPathName(toSave.getPath(), toSave
1356 .getName());
1357 //System.out.println(fullPath);
1358 String restoreVersion = fullPath + ".restore";
1359 File source = new File(fullPath);
1360 File dest = new File(restoreVersion);
1361
1362 FileChannel inputChannel = null;
1363 FileChannel outputChannel = null;
1364
1365 try{
1366 FileInputStream source_fis = new FileInputStream(source);
1367 inputChannel = source_fis.getChannel();
1368
1369 FileOutputStream dest_fos = new FileOutputStream(dest);
1370 outputChannel = dest_fos.getChannel();
1371
1372 outputChannel.transferFrom(inputChannel, 0, inputChannel.size());
1373 inputChannel.close();
1374 outputChannel.close();
1375 source_fis.close();
1376 dest_fos.close();
1377 }
1378 catch(Exception e){
1379 e.printStackTrace();
1380 }
1381
1382 return sf;
1383 }
1384
1385 /**
1386 * @param toAdd
1387 */
1388 public static void addToCache(Frame toAdd) {
1389 _Cache.put(toAdd.getName().toLowerCase(), toAdd);
1390 }
1391
1392 public static void ClearCache() {
1393 _Cache.clear();
1394 }
1395
1396 /**
1397 * Checks if a frame is in the current user profile frameset.
1398 *
1399 * @param toCheck
1400 * the frame to check
1401 * @return true if the frame is in the current user profile frameset
1402 */
1403 public static boolean isProfileFrame(Frame toCheck)
1404 {
1405 if (toCheck.getNumber() == 0 || toCheck.getFramesetName().equals(UserSettings.DEFAULT_PROFILE_NAME)) {
1406 return false;
1407 }
1408
1409 return toCheck.getPath().equals(PROFILE_PATH);
1410 // return toCheck.getFramesetName()
1411 // .equalsIgnoreCase(UserSettings.ProfileName);
1412 }
1413
1414 public static Frame LoadProfile(String profileName)
1415 {
1416 final String profilesLoc = System.getProperty("profiles.loc");
1417 if (profilesLoc != null) {
1418 return LoadFrame(profileName + "1", profilesLoc);
1419 } else {
1420 return LoadFrame(profileName + "1");
1421 }
1422 }
1423
1424 public static Frame CreateNewProfile(String userName, String profileName, Map<String, Setting> initialSettings, Map<String, Consumer<Frame>> toNotifyOnSet) throws InvalidFramesetNameException, ExistingFramesetException { //TODO: Investigate these throws, they never seem to be thrown up this far.
1425// Frame profile = CreateFrameset(username, PROFILE_PATH, true);
1426// if (profile != null) {
1427// FrameUtils.CreateDefaultProfile(username, profile, initialSettings, toNotifyOnSet);
1428// } else {
1429// System.err.println("An error occured while attempting to create the profile named: " + username);
1430// System.err.println("Unable to proceed.");
1431// System.exit(1);
1432// }
1433// return profile;
1434 if (profileName.equals(UserSettings.DEFAULT_PROFILE_NAME)) {
1435 System.err.println("FrameIO::CreateNewProfile::DEPRECATED::CreateNewProfile should no longer be called to create a default profile. Use FrameIO::CreateNewDefaultProfile instead.");
1436 return CreateNewDefaultProfile();
1437 } else {
1438 return ProfileManager.createProfile(userName, profileName, initialSettings, toNotifyOnSet);
1439 }
1440 }
1441
1442 public static Frame CreateNewDefaultProfile() throws InvalidFramesetNameException, ExistingFramesetException { //TODO: Investigate these throws, they never seem to be thrown up this far.
1443 ProfileManager.ensureDefaultProfile();
1444 return FrameIO.LoadFrame(UserSettings.DEFAULT_PROFILE_NAME + "1");
1445 }
1446
1447 /**
1448 * Reads the INF file that corresponds to the given Frame name
1449 *
1450 * @param framename
1451 * The Frame to lookup the INF file for
1452 * @throws IOException
1453 * Any exceptions encountered by the BufferedReader used to read
1454 * the INF.
1455 */
1456 public static int ReadINF(String path, String frameset, boolean update)
1457 throws IOException {
1458 assert (!frameset.endsWith("."));
1459 try {
1460 // read INF
1461 BufferedReader reader;
1462 try {
1463 // Check on the local drive
1464 reader = new BufferedReader(new FileReader(path
1465 + frameset.toLowerCase() + File.separator
1466 + INF_FILENAME));
1467 } catch (Exception e) {
1468 reader = new BufferedReader(new FileReader(path
1469 + frameset.toLowerCase() + File.separator
1470 + frameset.toLowerCase() + ".inf"));
1471 }
1472 String inf = reader.readLine();
1473 reader.close();
1474
1475 int next = Conversion.getFrameNumber(inf);
1476 // update INF file
1477 if (update) {
1478 try {
1479 WriteINF(path, frameset, frameset + (next + 1));
1480 } catch (IOException ioe) {
1481 ioe.printStackTrace();
1482 Logger.Log(ioe);
1483 }
1484 }
1485 return next;
1486 } catch (Exception e) {
1487 }
1488
1489 // Check peers
1490 return FrameShare.getInstance().getInfNumber(path, frameset, update);
1491 }
1492
1493 /**
1494 * Writes the given String out to the INF file corresponding to the current
1495 * frameset.
1496 *
1497 * @param toWrite
1498 * The String to write to the file.
1499 * @throws IOException
1500 * Any exception encountered by the BufferedWriter.
1501 */
1502 public static void WriteINF(String path, String frameset, String frameName)
1503 throws IOException {
1504 try {
1505 assert (!frameset.endsWith("."));
1506
1507 path += frameset.toLowerCase() + File.separator + INF_FILENAME;
1508
1509 BufferedWriter writer = new BufferedWriter(new FileWriter(path));
1510 writer.write(frameName);
1511 writer.close();
1512 } catch (Exception e) {
1513
1514 }
1515 }
1516
1517 public static boolean FrameIsCached(String name) {
1518 return _Cache.containsKey(name);
1519 }
1520
1521 /**
1522 * Gets a frame from the cache.
1523 *
1524 * @param name
1525 * The frame to get from the cache
1526 *
1527 * @return The frame from cache. Null if not cached.
1528 */
1529 public static Frame FrameFromCache(String name) {
1530 return _Cache.get(name);
1531 }
1532
1533 public static String ConvertToValidFramesetName(String toValidate) {
1534 assert (toValidate != null && toValidate.length() > 0);
1535
1536 StringBuffer result = new StringBuffer();
1537
1538 if (Character.isDigit(toValidate.charAt(0))) {
1539 result.append(FRAME_NAME_LAST_CHAR);
1540 }
1541
1542 boolean capital = false;
1543 for (int i = 0; i < toValidate.length()
1544 && result.length() < MAX_NAME_LENGTH; i++) {
1545 char cur = toValidate.charAt(i);
1546
1547 // capitalize all characters after spaces
1548 if (Character.isLetterOrDigit(cur)) {
1549 if (capital) {
1550 capital = false;
1551 result.append(Character.toUpperCase(cur));
1552 } else {
1553 result.append(cur);
1554 }
1555 } else {
1556 capital = true;
1557 }
1558 }
1559 assert (result.length() > 0);
1560 int lastCharIndex = result.length() - 1;
1561 if (!Character.isLetter(result.charAt(lastCharIndex))) {
1562 if (lastCharIndex == MAX_NAME_LENGTH - 1) {
1563 result.setCharAt(lastCharIndex, FRAME_NAME_LAST_CHAR);
1564 } else {
1565 result.append(FRAME_NAME_LAST_CHAR);
1566 }
1567 }
1568
1569 assert (isValidFramesetName(result.toString()));
1570 return result.toString();
1571 }
1572
1573 public static Frame CreateNewFrame(Item linker) throws RuntimeException {
1574 String title = linker.getName();
1575
1576 String templateLink = linker.getAbsoluteLinkTemplate();
1577 String framesetLink = linker.getAbsoluteLinkFrameset();
1578 String frameset = (framesetLink != null ? framesetLink : DisplayController
1579 .getCurrentFrame().getFramesetName());
1580
1581 Frame newFrame = FrameIO.CreateFrame(frameset, title, templateLink);
1582 return newFrame;
1583 }
1584
1585 public static Frame CreateNewFrame(Item linker, OnNewFrameAction action) throws RuntimeException {
1586 Frame newFrame = FrameIO.CreateNewFrame(linker);
1587 if(action != null) {
1588 action.exec(linker, newFrame);
1589 }
1590 return newFrame;
1591 }
1592
1593 /**
1594 * Creates a new Frameset on disk, including a .0, .1, and .inf files. The
1595 * Default.0 frame is copied to make the initial .0 and .1 Frames
1596 *
1597 * @param name
1598 * The Frameset name to use
1599 * @param zeroFrameItems TODO
1600 * @return The name of the first Frame in the newly created Frameset (the .1
1601 * frame)
1602 */
1603 public static Frame CreateNewFrameset(String name, Collection<Item> zeroFrameItems) throws Exception {
1604 String path = DisplayController.getCurrentFrame().getPath();
1605
1606 // if current frameset is profile directory change it to framesets
1607 if (path.equals(FrameIO.PROFILE_PATH)) {
1608 path = FrameIO.FRAME_PATH;
1609 }
1610
1611 Frame newFrame = FrameIO.CreateFrameset(name, path, zeroFrameItems);
1612
1613 if (newFrame == null) {
1614 // Cant create directories if the path is readonly or there is no
1615 // space available
1616 newFrame = FrameIO.CreateFrameset(name, FrameIO.FRAME_PATH, zeroFrameItems);
1617 }
1618
1619 if (newFrame == null) {
1620 // TODO handle running out of disk space here
1621 }
1622
1623 return newFrame;
1624 }
1625
1626 public static Frame CreateNewGroup(String name, Collection<Item> zeroFrameItems) {
1627 try {
1628 Frame oneFrame = FrameIO.CreateFrameset(name, FrameIO.GROUP_PATH, zeroFrameItems);
1629 oneFrame.setPermission(new PermissionTriple(UserAppliedPermission.full, UserAppliedPermission.none, UserAppliedPermission.none));
1630
1631 Text ownerAnnotation = oneFrame.createNewText("@Owner: " + UserSettings.UserName.get());
1632 ownerAnnotation.setPosition(100, 100);
1633 ownerAnnotation.setPermission(new PermissionTriple(UserAppliedPermission.full, UserAppliedPermission.none, UserAppliedPermission.none));
1634 Text membersAnnotation = oneFrame.createNewText("@Members: ");
1635 membersAnnotation.setPosition(100, 200);
1636
1637 FrameIO.SaveFrame(oneFrame);
1638
1639 FrameIO.LoadFrame(name + 0, FrameIO.GROUP_PATH).setPermission(new PermissionTriple(UserAppliedPermission.full, UserAppliedPermission.none, UserAppliedPermission.none));
1640
1641 return oneFrame;
1642 } catch (Exception e) {
1643 MessageBay.displayMessage("Unable to create group with name: " + name + ". See console for more details.");
1644 e.printStackTrace();
1645 return null;
1646 }
1647 }
1648
1649 /**
1650 *
1651 * @param frameset
1652 * @return
1653 */
1654 public static int getLastNumber(String frameset) { // Rob thinks it might
1655 // have been
1656 // GetHighestNumExFrame
1657 // TODO minimise the number of frames being read in!!
1658 int num = -1;
1659
1660 Frame zero = LoadFrame(frameset + "0");
1661
1662 // the frameset does not exist (or has no 0 frame)
1663 if (zero == null) {
1664 return -1;
1665 }
1666
1667 try {
1668 num = ReadINF(zero.getPath(), frameset, false);
1669 } catch (IOException e) {
1670 // TODO Auto-generated catch block
1671 // e.printStackTrace();
1672 }
1673
1674 /*
1675 * Michael doesnt think the code below is really needed... it will just
1676 * slow things down when we are reading frames over a network***** for (;
1677 * num >= 0; num--) { System.out.println("This code is loading frames to
1678 * find the highest existing frame..."); if (LoadFrame(frameset + num) !=
1679 * null) break; }
1680 */
1681
1682 return num;
1683 }
1684
1685 /**
1686 * Checks if a given frameset is accessable.
1687 *
1688 * @param framesetName
1689 * @return
1690 */
1691 public static boolean canAccessFrameset(String framesetName) {
1692 framesetName = framesetName.toLowerCase();
1693 for (String path : FolderSettings.FrameDirs.getAbsoluteDirs()) {
1694 if (canAccessFrameset(framesetName, Paths.get(path))) {
1695 return true;
1696 }
1697 }
1698 return false;
1699 }
1700
1701 public static boolean canAccessFrameset(String framesetName, Path path) {
1702 File framesetDir = path.resolve(framesetName).toFile();
1703 if (framesetDir.exists() && framesetDir.isDirectory()) {
1704 return true;
1705 } else {
1706 return false;
1707 }
1708 }
1709
1710 public static Frame CreateFrameset(String frameset, String path, boolean recreate, Collection<Item> zeroFrameItems) throws InvalidFramesetNameException, ExistingFramesetException {
1711 String conversion = frameset + " --> ";
1712
1713 if (!isValidFramesetName(frameset)) {
1714 throw new InvalidFramesetNameException(frameset);
1715 }
1716
1717 if (!recreate && FrameIO.canAccessFrameset(frameset)) {
1718 throw new ExistingFramesetException(frameset);
1719 }
1720
1721 conversion += frameset;
1722 Logger.Log(Logger.SYSTEM, Logger.NEW_FRAMESET, "Frameset Name: "
1723 + conversion);
1724 conversion = frameset;
1725
1726 /**
1727 * TODO: Update this to exclude any\all invalid filename characters
1728 */
1729 // ignore annotation character
1730 if (frameset.startsWith("@")) {
1731 frameset = frameset.substring(1);
1732 }
1733
1734 conversion += " --> " + frameset;
1735 Logger.Log(Logger.SYSTEM, Logger.NEW_FRAMESET, "Name: " + conversion);
1736
1737 // create the new Frameset directory
1738 File dir = new File(path + frameset.toLowerCase() + File.separator);
1739
1740 // If the directory doesnt already exist then create it...
1741 if (!dir.exists()) {
1742 if (!dir.mkdirs()) {
1743 /*
1744 * If the directory does not exist, but could not be created then there is something wrong.
1745 * Prior to May 2019 the only known reason for this was because the disk could be full.
1746 * Since then, we have discovered that null can occur when working with Google file stream.
1747 * A directory can return false to an existence check, but then fail to create the directory
1748 * due to it already existing because of sync issues. While we have not confirmed, this may
1749 * be the case with other network drives as well.
1750 */
1751 System.err.println("Failed to create directory for frameset: " + frameset);
1752 return null;
1753 }
1754 }
1755
1756 // create the new INF file
1757 try {
1758 WriteINF(path, frameset, frameset + '1');
1759 } catch (IOException ioe) {
1760 ioe.printStackTrace();
1761 Logger.Log(ioe);
1762 }
1763
1764 SuspendCache();
1765 // copy the default .0 and .1 files
1766 Frame base = null;
1767 try {
1768 base = LoadFrame(TemplateSettings.DefaultFrame.get());
1769 } catch (Exception e) {
1770 }
1771 // The frame may not be accessed for various reasons... in all these
1772 // cases just create a new one
1773 if (base == null) {
1774 base = new Frame();
1775 }
1776
1777 ResumeCache();
1778
1779 // 0 frame
1780 base.reset();
1781 base.resetDateCreated();
1782 base.setFrameset(frameset);
1783 base.setFrameNumber(0);
1784 base.setOwner(UserSettings.UserName.get());
1785 base.setTitle(base.getFramesetName() + "0");
1786 base.setPath(path);
1787 base.change();
1788 if (zeroFrameItems != null) {
1789 base.addAllItems(zeroFrameItems);
1790 }
1791 SaveFrame(base, false);
1792
1793 // 1 frame
1794 base.reset();
1795 base.resetDateCreated();
1796 base.setFrameNumber(1);
1797 base.setOwner(UserSettings.UserName.get());
1798 base.setTitle(frameset);
1799 base.change();
1800 SaveFrame(base, true);
1801
1802 FrameIO.setSavedProperties(base);
1803
1804 Logger.Log(Logger.SYSTEM, Logger.NEW_FRAMESET, "Created new frameset: " + frameset);
1805
1806 return base;
1807 }
1808
1809 /**
1810 * Tests if a frameset name is valid. That is it must begin and end with a
1811 * letter and contain only letters and digits in between.
1812 *
1813 * @param frameset
1814 * the name to be tested
1815 * @return true if the frameset name is valid
1816 */
1817 public static boolean isValidFramesetName(String frameset) {
1818 if (frameset == null) {
1819 return false;
1820 }
1821
1822 int nameLength = frameset.length();
1823 if (frameset.length() <= 0 || nameLength > MAX_NAME_LENGTH) {
1824 return false;
1825 }
1826
1827 int lastCharIndex = nameLength - 1;
1828
1829 if (!Character.isLetter(frameset.charAt(0))
1830 || !Character.isLetter(frameset.charAt(lastCharIndex))) {
1831 return false;
1832 }
1833
1834 for (int i = 1; i < lastCharIndex; i++) {
1835 if (!isValidFrameNameChar(frameset.charAt(i))) {
1836 return false;
1837 }
1838 }
1839 return true;
1840 }
1841
1842 public static boolean deleteFrameset(String framesetName) {
1843 return moveFrameset(framesetName, FrameIO.TRASH_PATH, true);
1844 }
1845
1846 public static boolean moveFrameset(String framesetName, String destinationFolder, boolean override) {
1847 if (!FrameIO.canAccessFrameset(framesetName)) {
1848 return false;
1849 }
1850 // Clear the cache
1851 _Cache.clear();
1852
1853 // Search all the available directories for the directory
1854 for (String path : FolderSettings.FrameDirs.getAbsoluteDirs()) {
1855 return moveFrameset(framesetName, path, destinationFolder, override);
1856 }
1857 return false;
1858 }
1859
1860 public static boolean moveFrameset(String framesetName, String path, String destinationFolder, boolean override) {
1861 String source = path + framesetName.toLowerCase() + File.separator;
1862 File framesetDirectory = new File(source);
1863 // Once we have found the directory move it
1864 if (framesetDirectory.exists()) {
1865 String destPath = destinationFolder
1866 + framesetName.toLowerCase();
1867 int copyNumber = 1;
1868 File dest = new File(destPath + File.separator);
1869 // Create the destination folder if it doesnt already exist
1870 if (!dest.getParentFile().exists()) {
1871 dest.mkdirs();
1872 }
1873 // If a frameset with the same name is already in the
1874 // destination add
1875 // a number to the end
1876 while (dest.exists() && !override) {
1877 dest = new File(destPath + ++copyNumber + File.separator);
1878 }
1879 try {
1880 moveFileTree(framesetDirectory.toPath(), dest.toPath());
1881 } catch (IOException e) {
1882 e.printStackTrace();
1883 return false;
1884 }
1885
1886 for (File f : framesetDirectory.listFiles()) {
1887 if (!f.delete()) {
1888 return false;
1889 }
1890 }
1891 if (!framesetDirectory.delete()) {
1892 return false;
1893 }
1894 return true;
1895 } else {
1896 return false;
1897 }
1898 }
1899
1900 public static boolean CopyFrameset(String framesetToCopy,
1901 String copiedFrameset) throws Exception {
1902 if (!FrameIO.canAccessFrameset(framesetToCopy)) {
1903 return false;
1904 }
1905 if (FrameIO.canAccessFrameset(copiedFrameset)) {
1906 return false;
1907 }
1908 // search through all the directories to find the frameset we are
1909 // copying
1910 for (String path : FolderSettings.FrameDirs.getAbsoluteDirs()) {
1911 String source = path + framesetToCopy.toLowerCase()
1912 + File.separator;
1913 File framesetDirectory = new File(source);
1914 if (framesetDirectory.exists()) {
1915 // copy the frameset
1916 File copyFramesetDirectory = new File(path
1917 + copiedFrameset.toLowerCase() + File.separator);
1918 if (!copyFramesetDirectory.mkdirs()) {
1919 return false;
1920 }
1921 // copy each of the frames
1922 for (File f : framesetDirectory.listFiles()) {
1923 // Ignore hidden files
1924 if (f.getName().charAt(0) == '.') {
1925 continue;
1926 }
1927 String copyPath = copyFramesetDirectory.getAbsolutePath()
1928 + File.separator + f.getName();
1929 FrameIO.copyFile(f.getAbsolutePath(), copyPath);
1930 }
1931 return true;
1932 }
1933 }
1934 return false;
1935 }
1936
1937 /**
1938 * Copies a file from one location to another.
1939 *
1940 * @param existingFile
1941 * @param newFileName
1942 * @throws Exception
1943 */
1944 public static void copyFile(String existingFile, String newFileName)
1945 throws IOException {
1946 FileInputStream is = new FileInputStream(existingFile);
1947 FileOutputStream os = new FileOutputStream(newFileName, false);
1948 int data;
1949 while ((data = is.read()) != -1) {
1950 os.write(data);
1951 }
1952 os.flush();
1953 os.close();
1954 is.close();
1955 }
1956
1957 /**
1958 * Saves a frame regardless of whether or not the frame is marked as having
1959 * been changed.
1960 *
1961 * @param frame
1962 * the frame to save
1963 * @return the contents of the frame or null if it could not be saved
1964 */
1965 public static String ForceSaveFrame(Frame frame) {
1966 frame.change();
1967 return SaveFrame(frame, false);
1968 }
1969
1970 public static boolean isValidLink(String frameName) {
1971 return frameName == null || isPositiveInteger(frameName)
1972 || isValidFrameName(frameName);
1973 }
1974
1975 public static void SavePublicFrame(String peerName, String frameName,
1976 int version, BufferedReader packetContents) {
1977 // TODO handle versioning - add version to the header
1978 // Remote user uploads version based on an old version
1979
1980 // Remove it from the cache so that next time it is loaded we get the up
1981 // todate version
1982 _Cache.remove(frameName.toLowerCase());
1983
1984 // Save to file
1985 String filename = PUBLIC_PATH + Conversion.getFramesetName(frameName)
1986 + File.separator + Conversion.getFrameNumber(frameName)
1987 + ExpReader.EXTENTION;
1988
1989 File file = new File(filename);
1990 // Ensure the file exists
1991 if (file.exists()) {
1992 // Check the versions
1993 int savedVersion = ExpReader.getVersion(filename);
1994
1995 if (savedVersion > version) {
1996 // remove this frame from the cache if it is there
1997 // This will make sure links to the original are set correctly
1998 // _Cache.remove(frameName.toLowerCase());
1999
2000 int nextNum = 0;
2001 try {
2002 nextNum = ReadINF(PUBLIC_PATH, Conversion
2003 .getFramesetName(frameName), false) + 1;
2004 } catch (IOException e) {
2005 e.printStackTrace();
2006 }
2007
2008 String newName = Conversion.getFramesetName(frameName)
2009 + nextNum;
2010 filename = PUBLIC_PATH + Conversion.getFramesetName(frameName)
2011 + File.separator + nextNum + ExpReader.EXTENTION;
2012
2013 // Show the messages alerting the user
2014 Text originalMessage = new Text(-1);
2015 originalMessage.setColor(MessageBay.ERROR_COLOR);
2016 originalMessage.setText(frameName + " was edited by "
2017 + peerName);
2018 originalMessage.setLink(frameName);
2019 Text yourMessage = new Text(-1);
2020 yourMessage.setColor(MessageBay.ERROR_COLOR);
2021 yourMessage.setText("Their version was renamed " + newName);
2022 yourMessage.setLink(newName);
2023 MessageBay.displayMessage(originalMessage);
2024 MessageBay.displayMessage(yourMessage);
2025
2026 Frame editedFrame = FrameIO.LoadFrame(frameName);
2027
2028 FrameShare.getInstance().sendMessage(
2029 frameName + " was recently edited by "
2030 + editedFrame.getLastModifyUser(), peerName);
2031 FrameShare.getInstance().sendMessage(
2032 "Your version was renamed " + newName, peerName);
2033 }
2034 }
2035
2036 // Save the new version
2037 try {
2038 // FileWriter fw = new FileWriter(file);
2039
2040 // Open an Output Stream Writer to set encoding
2041 OutputStream fout = new FileOutputStream(file);
2042 OutputStream bout = new BufferedOutputStream(fout);
2043 Writer fw = new OutputStreamWriter(bout, "UTF-8");
2044
2045 String nextLine = null;
2046 while ((nextLine = packetContents.readLine()) != null) {
2047 fw.write(nextLine + '\n');
2048 }
2049 fw.flush();
2050 fw.close();
2051 MessageBay.displayMessage("Saved remote frame: " + frameName);
2052 } catch (IOException e) {
2053 MessageBay.errorMessage("Error remote saving " + frameName + ": "
2054 + e.getMessage());
2055 e.printStackTrace();
2056 }
2057 }
2058
2059 public static void setSavedProperties(Frame toSave) {
2060 toSave.setLastModifyDate(Formatter.getDateTime(), System.currentTimeMillis());
2061 toSave.setLastModifyUser(UserSettings.UserName.get());
2062 toSave.setVersion(toSave.getVersion() + 1);
2063 Time darkTime = new Time(SessionStats.getFrameDarkTime().getTime()
2064 + toSave.getDarkTime().getTime());
2065 Time activeTime = new Time(SessionStats.getFrameActiveTime().getTime()
2066 + toSave.getActiveTime().getTime());
2067 toSave.setDarkTime(darkTime);
2068 toSave.setActiveTime(activeTime);
2069 }
2070
2071 public static boolean personalResourcesExist(String username) {
2072 Path personalResources = Paths.get(FrameIO.PARENT_FOLDER).resolve("resources-" + username);
2073 File personalResourcesFile = personalResources.toFile();
2074 boolean directoryExists = personalResourcesFile.exists() && personalResourcesFile.isDirectory();
2075 return directoryExists;
2076 }
2077
2078 public static Path setupPersonalResources(String username) {
2079 Path personalResources = Paths.get(FrameIO.PARENT_FOLDER).resolve("resources-" + username);
2080 personalResources.toFile().mkdir();
2081 File[] globalResourcesToCopy = Paths.get(FrameIO.RESOURCES_PRIVATE_PATH).toFile().listFiles();
2082
2083 try {
2084 for (File toCopy: globalResourcesToCopy) {
2085 Path p = Paths.get(toCopy.getAbsolutePath());
2086 if (!p.getFileName().toString().equals(".res") && !p.getFileName().toString().equals("about")) {
2087 moveFileTree(p.toAbsolutePath(), personalResources.resolve(p.getFileName()));
2088 }
2089 }
2090 } catch (IOException e) {
2091 e.printStackTrace();
2092 personalResources = null;
2093 }
2094
2095 return personalResources;
2096 }
2097
2098 public static void migrateFrame(Frame toMigrate, Path destinationDirectory) {
2099 Path source = Paths.get(toMigrate.getFramePathReal());
2100 String destination = source.relativize(destinationDirectory).toString().substring(3).replace(File.separator, "/");
2101 try {
2102 Files.move(source, destinationDirectory);
2103 } catch (IOException e) {
2104 System.err.println("FrameIO::migrateFrame: failed to migrate from to new location. Message: " + e.getMessage());
2105 return;
2106 }
2107 try {
2108 FileWriter out = new FileWriter(source.toFile());
2109 out.write("REDIRECT:" + destination);
2110 out.flush();
2111 out.close();
2112 } catch (IOException e) {
2113 System.err.println("FrameIO::migrateFrame: failed to update file [" + source + "] to redirect to [" + destination + "] following migration. Message: " + e.getMessage());
2114 }
2115 }
2116
2117 private static void moveFileTree(Path source, Path target) throws IOException {
2118 if (source.toFile().isDirectory()) {
2119 if (!target.toFile().exists()) {
2120 Files.copy(source, target);
2121 }
2122 File[] files = source.toFile().listFiles();
2123 for (File file: files) {
2124 Path asPath = Paths.get(file.getAbsolutePath());
2125 moveFileTree(asPath, target.resolve(asPath.getFileName()));
2126 }
2127 } else {
2128 Files.copy(source, target, StandardCopyOption.REPLACE_EXISTING);
2129 }
2130 }
2131}
Note: See TracBrowser for help on using the repository browser.