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

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

Implementation of ProfileManager. Refactor + additional content for how new profiles are created. The refactoring split out the creation of the default profile from user profiles. Refactoring revealed a long term bug that was causing user profiles to generate with incorrect information. The additional content fixed this bug by introducing the ${USER.NAME} variable, so that the default profile frameset can specify resource locations located in the users resource directory.

org.expeditee.auth.AuthenticatorBrowser
org.expeditee.auth.account.Create
org.expeditee.gui.Browser
org.expeditee.gui.management.ProfileManager
org.expeditee.setting.DirectoryListSetting
org.expeditee.setting.ListSetting
org.expeditee.settings.UserSettings

Implementation of ResourceManager as a core location to get resources from the file system. Also the additional variable ${CURRENT_FRAMESET} to represent the current frameset, so that images can be stored in the directory of the current frameset. This increases portability of framesets.

org.expeditee.gui.FrameIO
org.expeditee.gui.management.ResourceManager
org.expeditee.gui.management.ResourceUtil
Audio:

#NB: Audio used to only operate on a single directory. This has been updated to work in a same way as images. That is: when you ask for a specific resouce, it looks to the user settings to find a sequence of directories to look at in order until it manages to find the desired resource.


There is still need however for a single(ish) source of truth for the .banks and .mastermix file. Therefore these files are now always located in resource-<username>\audio.
org.apollo.agents.MelodySearch
org.apollo.audio.structure.AudioStructureModel
org.apollo.audio.util.MultiTrackPlaybackController
org.apollo.audio.util.SoundDesk
org.apollo.gui.FrameLayoutDaemon
org.apollo.io.AudioPathManager
org.apollo.util.AudioPurger
org.apollo.widgets.FramePlayer
org.apollo.widgets.SampledTrack

Images:

org.expeditee.items.ItemUtils

Frames:

org.expeditee.gui.FrameIO

Fixed a error in the FramePlayer class caused by an incorrect use of toArray().

org.apollo.widgets.FramePlayer


Added several short cut keys to allow for the Play/Pause (Ctrl + P), mute (Ctrl + M) and volume up/down (Ctrl + +/-) when hovering over SampledTrack widgets.

org.apollo.widgets.SampledTrack


Changed the way that Authenticate.login parses the new users profile to be more consistance with other similar places in code.

org.expeditee.auth.account.Authenticate


Encapsulated _body, _surrogateItemsBody and _primaryItemsBody in Frame class. Also changed getBody function to take a boolean flag as to if it should respect the current surrogate mode. If it should then it makes sure that labels have not changed since last time getBody was called.

org.expeditee.gui.Frame

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