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

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

Renamed MailMode action to ToggleBay
Renamed FrameCreator enums to more desirable names (David request)
Created test for altered functionality of FrameCreator as documented below.

FrameCreator now more cleanly obeys the specification of the enum parameter to its constructor. For example, override existing frameset ensures that the old frameset has been deleted (moved to trash).

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