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

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

Renamed Frame.getItems() to Frame.getSortedItems() to better represent its functionality.

-> org.apollo.ApolloGestureActions
-> org.apollo.ApolloSystem
-> org.expeditee.actions.Actions
-> org.expeditee.actions.Debug
-> org.expeditee.actions.ExploratorySearchActions
-> org.expeditee.actions.JfxBrowserActions
-> org.expeditee.actions.Misc
-> org.expeditee.actions.Navigation
-> org.expeditee.actions.ScriptBase
-> org.expeditee.actions.Simple
-> org.expeditee.agents.ComputeTree
-> org.expeditee.agents.CopyTree
-> org.expeditee.agents.DisplayComet
-> org.expeditee.agents.DisplayTree
-> org.expeditee.agents.DisplayTreeLeaves
-> org.expeditee.agents.GraphFramesetLinks
-> org.expeditee.agents.TreeProcessor
-> org.expeditee.gio.gesture.StandardGestureActions
-> org.expeditee.gui.DisplayController
-> org.expeditee.gui.FrameCreator
-> org.expeditee.gui.FrameIO
-> org.expeditee.io.DefaultTreeWriter
-> org.expeditee.io.JavaWriter
-> org.expeditee.io.PDF2Writer
-> org.expeditee.io.TXTWriter
-> org.expeditee.io.WebParser
-> org.expeditee.io.flowlayout.XGroupItem
-> org.expeditee.items.Dot
-> org.expeditee.items.Item
-> org.expeditee.items.ItemUtils
-> org.expeditee.network.FrameShare
-> org.expeditee.stats.TreeStats


Created ItemsList class to wrap ArrayList<Item>. Frames now use this new class to store its body list (used for display) as well as its primaryBody and surrogateBody.

-> org.expeditee.agents.Format
-> org.expeditee.agents.HFormat
-> org.expeditee.gio.gesture.StandardGestureActions
-> org.expeditee.gui.Frame
-> org.expeditee.gui.FrameUtils


Refactorted Frame.setResort(bool) to Frame.invalidateSorted() to better function how it is intended to with a more accurate name.

-> org.expeditee.agents.Sort


When writing out .exp files and getting attributes to respond to LEFT + RIGHT click, boolean items are by default true. This has always been the case. An ammendment to this is that defaults can now be established.
Also added 'EnterClick' functionality. If cursored over a item with this property and you press enter, it acts as if you have clicked on it instead.

-> org.expeditee.assets.resources-public.framesets.authentication.1.exp to 6.exp
-> org.expeditee.gio.gesture.StandardGestureActions
-> org.expeditee.gio.input.KBMInputEvent
-> org.expeditee.gio.javafx.JavaFXConversions
-> org.expeditee.gio.swing.SwingConversions
-> org.expeditee.gui.AttributeUtils
-> org.expeditee.io.Conversion
-> org.expeditee.io.DefaultFrameWriter
-> org.expeditee.items.Item


Fixed a bug caused by calling Math.abs on Integer.MIN_VALUE returning unexpected result. Due to zero being a thing, you cannot represent Math.abs(Integer.MIN_VALUE) in a Integer object. The solution is to use Integer.MIN_VALUE + 1 instead of Integer.MIN_VALUE.

-> org.expeditee.core.bounds.CombinationBounds
-> org.expeditee.io.flowlayout.DimensionExtent


Recoded the contains function in EllipticalBounds so that intersection tests containing circles work correctly.

-> org.expeditee.core.bounds.EllipticalBounds


Added toString() to PolygonBounds to allow for useful printing during debugging.

-> org.expeditee.core.bounds.PolygonBounds

Implemented Surrogate Mode!

-> org.expeditee.encryption.io.EncryptedExpReader
-> org.expeditee.encryption.io.EncryptedExpWriter
-> org.expeditee.encryption.items.surrogates.EncryptionDetail
-> org.expeditee.encryption.items.surrogates.Label
-> org.expeditee.gui.FrameUtils
-> org.expeditee.gui.ItemsList
-> org.expeditee.items.Item
-> org.expeditee.items.Text


???? Use Integer.MAX_VALUE cast to a float instead of Float.MAX_VALUE. This fixed some bug which I cannot remember.

-> org.expeditee.gio.TextLayoutManager
-> org.expeditee.gio.swing.SwingTextLayoutManager


Improved solution for dealing with the F10 key taking focus away from Expeditee due to it being a assessibility key.

-> org.expeditee.gio.swing.SwingInputManager


Renamed variable visibleItems in FrameGraphics.paintFrame to itemsToPaintCanditates to better represent functional intent.

-> org.expeditee.gui.FrameGraphics


Improved checking for if personal resources exist before recreating them

-> org.expeditee.gui.FrameIO


Repeated messages to message bay now have a visual feedback instead of just a beep. This visual feedback is in the form of a count of the amount of times it has repeated.

-> org.expeditee.gui.MessageBay


Updated comment on the Vector class to explain what vectors are.

-> org.expeditee.gui.Vector


Added constants to represent all of the property keys in DefaultFrameReader and DefaultFrameWriter.

-> org.expeditee.io.DefaultFrameReader
-> org.expeditee.io.DefaultFrameWriter


Updated the KeyList setting to be more heirarcial with how users will store their Secrets.

-> org.expeditee.settings.identity.secrets.KeyList

File size: 64.3 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.io.Conversion;
60import org.expeditee.io.ExpReader;
61import org.expeditee.io.ExpWriter;
62import org.expeditee.io.FrameReader;
63import org.expeditee.io.FrameWriter;
64import org.expeditee.io.KMSReader;
65import org.expeditee.io.KMSWriter;
66import org.expeditee.items.Item;
67import org.expeditee.items.ItemUtils;
68import org.expeditee.items.Justification;
69import org.expeditee.items.PermissionTriple;
70import org.expeditee.items.Text;
71import org.expeditee.items.UserAppliedPermission;
72import org.expeditee.network.FrameShare;
73import org.expeditee.setting.Setting;
74import org.expeditee.settings.UserSettings;
75import org.expeditee.settings.folders.FolderSettings;
76import org.expeditee.settings.templates.TemplateSettings;
77import org.expeditee.stats.Formatter;
78import org.expeditee.stats.Logger;
79import org.expeditee.stats.SessionStats;
80
81/**
82 * This class provides static methods for all saving and loading of Frames
83 * to\from disk. This class also handles any caching of previously loaded
84 * Frames.
85 *
86 * @author jdm18
87 *
88 */
89
90public class FrameIO {
91
92 private static final char FRAME_NAME_LAST_CHAR = 'A';
93
94 // The parent path that all others are relative to. Also referred to as Expeditee Home.
95 public static String PARENT_FOLDER;
96
97 public static String PROFILE_PATH;
98 public static String FRAME_PATH;
99 public static String IMAGES_PATH;
100 public static String AUDIO_PATH;
101 public static String PUBLIC_PATH;
102 public static String TRASH_PATH;
103 public static String FONT_PATH;
104 public static String DICT_PATH;
105 public static String EXPORTS_PATH;
106 public static String STATISTICS_PATH;
107 public static String LOGS_PATH;
108 public static String MESSAGES_PATH;
109 public static String MAIL_PATH;
110 public static String SHARED_FRAMESETS_PATH;
111 public static String CONTACTS_PATH;
112 public static String RESOURCES_PRIVATE_PATH;
113 public static String RESOURCES_PATH;
114 public static String FRAME_PRIVATE_PATH;
115 public static String IMAGES_PRIVATE_PATH;
116 public static String AUDIO_PRIVATE_PATH;
117 public static String HELP_PRIVATE_PATH;
118 public static String HELP_PATH;
119 public static String DEAD_DROPS_PATH;
120 public static String GROUP_PATH;
121
122 // Paths that appear to be unused.
123 public static String TEMPLATES_PATH;
124
125 // Variables for controlling cache functionality.
126 public static final int MAX_NAME_LENGTH = 64;
127 public static final int MAX_CACHE = 100;
128 private static HashMap<String, Frame> _Cache = new FrameCache();
129 private static final boolean ENABLE_CACHE = true;
130 private static boolean _UseCache = true;
131 private static boolean _SuspendedCache = false;
132
133 private static final String INF_FILENAME = "frame.inf";
134
135 public static void changeParentAndSubFolders(String newFolder) {
136 // Partial Paths
137 PARENT_FOLDER = newFolder;
138 PUBLIC_PATH = PARENT_FOLDER + "public" + File.separator;
139 TRASH_PATH = PARENT_FOLDER + "trash" + File.separator;
140 PROFILE_PATH = PARENT_FOLDER + "profiles" + File.separator;
141 EXPORTS_PATH = PARENT_FOLDER + "exports" + File.separator;
142 STATISTICS_PATH = PARENT_FOLDER + "statistics" + File.separator;
143 LOGS_PATH = PARENT_FOLDER + "logs" + File.separator;
144
145 String resourcesPublicPath = PARENT_FOLDER + "resources-public" + File.separator;
146 String resourcesPrivateIndividualPath = PARENT_FOLDER + "resources-" + UserSettings.UserName.get() + File.separator;
147
148 if (UserSettings.PublicAndPrivateResources) {
149 // Paths for the new regime
150 FONT_PATH = resourcesPublicPath + "fonts" + File.separator;
151 DICT_PATH = resourcesPublicPath + "dict" + File.separator;
152 HELP_PATH = resourcesPublicPath + "documentation" + File.separator;
153 HELP_PRIVATE_PATH = resourcesPrivateIndividualPath + "documentation" + File.separator;
154 FRAME_PATH = resourcesPublicPath + "framesets" + File.separator;
155 FRAME_PRIVATE_PATH = resourcesPrivateIndividualPath + "framesets" + File.separator;
156 MESSAGES_PATH = resourcesPrivateIndividualPath + "messages" + File.separator;
157 IMAGES_PATH = resourcesPublicPath + "images" + File.separator;
158 IMAGES_PRIVATE_PATH = resourcesPrivateIndividualPath + "images" + File.separator;
159 AUDIO_PATH = resourcesPublicPath + "audio" + File.separator;
160 AUDIO_PRIVATE_PATH = resourcesPrivateIndividualPath + "audio" + File.separator;
161 GROUP_PATH = resourcesPrivateIndividualPath + "groups" + File.separator;
162
163 // Used only when extracting resources (when expeditee is run for first time)
164 RESOURCES_PRIVATE_PATH = PARENT_FOLDER + "resources-private" + File.separator;
165
166 if (AuthenticatorBrowser.isAuthenticated()) {
167 // Paths for the new regime while authenticated
168 SHARED_FRAMESETS_PATH = resourcesPrivateIndividualPath + "framesets-shared" + File.separator;
169 DEAD_DROPS_PATH = resourcesPrivateIndividualPath + "deaddrops" + File.separator;
170 CONTACTS_PATH = resourcesPrivateIndividualPath + "contacts" + File.separator;
171 MAIL_PATH = resourcesPrivateIndividualPath + "mail" + File.separator;
172 } else {
173 SHARED_FRAMESETS_PATH = null;
174 DEAD_DROPS_PATH = null;
175 CONTACTS_PATH = null;
176 MAIL_PATH = null;
177 }
178 } else {
179 // Paths for the old regime
180 FONT_PATH = PARENT_FOLDER + "fonts" + File.separator;
181 DICT_PATH = PARENT_FOLDER + "dict" + File.separator;
182 HELP_PATH = PARENT_FOLDER + "documentation" + File.separator;
183 FRAME_PATH = PARENT_FOLDER + "framesets" + File.separator;
184 MESSAGES_PATH = PARENT_FOLDER + "messages" + File.separator;
185 IMAGES_PATH = PARENT_FOLDER + "images" + File.separator;
186 AUDIO_PATH = PARENT_FOLDER + "audio" + File.separator;
187 GROUP_PATH = PARENT_FOLDER + "groups" + File.separator;
188
189 // These paths are not used by old regime.
190 HELP_PRIVATE_PATH = null;
191 FRAME_PRIVATE_PATH = null;
192 IMAGES_PRIVATE_PATH = null;
193 AUDIO_PRIVATE_PATH = null;
194 // - 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.
195 RESOURCES_PRIVATE_PATH = null;
196
197 if (AuthenticatorBrowser.isAuthenticated()) {
198 // Paths for the old regime while authenticated
199 SHARED_FRAMESETS_PATH = PARENT_FOLDER + "framesets-shared" + File.separator;
200 DEAD_DROPS_PATH = PARENT_FOLDER + "deaddrops" + File.separator;
201 CONTACTS_PATH = PARENT_FOLDER + "contacts" + File.separator;
202 MAIL_PATH = PARENT_FOLDER + "mail" + File.separator;
203 } else {
204 SHARED_FRAMESETS_PATH = null;
205 DEAD_DROPS_PATH = null;
206 CONTACTS_PATH = null;
207 MAIL_PATH = null;
208 }
209 }
210
211 System.err.println("**** FrameIO::changeParentAndSubFolder(): Calling AudioPathManger.changeParentAndSubFolder()");
212 AudioPathManager.changeParentAndSubFolders(newFolder);
213 }
214
215 // All methods are static, this should not be instantiated
216 private FrameIO() {
217 }
218
219 public static boolean isCacheOn() {
220 return _UseCache && ENABLE_CACHE;
221 }
222
223 public static void Precache(String framename) {
224 // if the cache is turned off, do nothing
225 if (!isCacheOn()) {
226 return;
227 }
228
229 // if the frame is already in the cache, do nothing
230 if (_Cache.containsKey(framename.toLowerCase())) {
231 return;
232 }
233
234 // otherwise, load the frame and put it in the cache
235 Logger.Log(Logger.SYSTEM, Logger.LOAD, "Precaching " + framename + ".");
236
237 // do not display errors encountered to the user
238 // (they will be shown at load time)
239 MessageBay.suppressMessages(true);
240 // loading automatically caches the frame is caching is turned on
241 LoadFromDisk(framename, null, false);
242 MessageBay.suppressMessages(false);
243 }
244
245 /**
246 * Checks if a string is a representation of a positive integer.
247 *
248 * @param s
249 * @return true if s is a positive integer
250 */
251 public static boolean isPositiveInteger(String s) {
252 if (s == null || s.length() == 0) {
253 return false;
254 }
255
256 for (int i = 0; i < s.length(); i++) {
257 if (!Character.isDigit(s.charAt(i))) {
258 return false;
259 }
260 }
261 return true;
262 }
263
264 /**
265 * Loads a frame with the specified name.
266 * By using a dot separated framename, users are able to specify the path to find the frameset in.
267 * @param frameName The frame to load.
268 * @return the loaded frame
269 */
270 public static Frame LoadFrame(String frameName) {
271 if (frameName.contains(".")) {
272 String[] split = frameName.split("\\.");
273 String[] pathSplit = Arrays.copyOfRange(split, 0, split.length - 1);
274 String name = split[split.length - 1];
275 String path = Arrays.asList(pathSplit).stream().collect(Collectors.joining(File.separator));
276 return LoadFrame(name, Paths.get(FrameIO.PARENT_FOLDER).resolve(path).toString() + File.separator, false);
277 } else {
278 return LoadFrame(frameName, null, false);
279 }
280 }
281
282 public static Frame LoadFrame(String frameName, String path) {
283 return LoadFrame(frameName, path, false);
284 }
285
286 public static Frame LoadFrame(String frameName, String path, boolean ignoreAnnotations) {
287 if (!isValidFrameName(frameName)) {
288 return null;
289 }
290
291 String frameNameLower = frameName.toLowerCase();
292 // first try reading from cache
293 if (isCacheOn() && _Cache.containsKey(frameNameLower)) {
294 Logger.Log(Logger.SYSTEM, Logger.LOAD, "Loading " + frameName + " from cache.");
295 Frame frame = _Cache.get(frameNameLower);
296
297 // if frame in cache is older than the one on disk then don't use the cached one
298 File file = new File(frame.getFramePathReal());
299 long lastModified = file.lastModified();
300 if (lastModified <= frame.getLastModifyPrecise()) {
301 return frame;
302 }
303 }
304
305 Logger.Log(Logger.SYSTEM, Logger.LOAD, "Loading " + frameName
306 + " from disk.");
307
308 Frame fromDisk = LoadFromDisk(frameName, path, ignoreAnnotations);
309 return fromDisk;
310 }
311
312 //Loads the 'restore' version of a frame if there is one
313 public static Frame LoadRestoreFrame(Frame frameToRestore) {
314
315 String fullPath = getFrameFullPathName(frameToRestore.getPath(), frameToRestore
316 .getName());
317 //System.out.println("fullpath: " + fullPath);
318 String restoreVersion = fullPath + ".restore";
319 //System.out.println("restoreversion" + restoreVersion);
320 File source = new File(restoreVersion);
321 File dest = new File(fullPath);
322
323 FileChannel inputChannel = null;
324 FileChannel outputChannel = null;
325
326 try{
327 FileInputStream source_fis = new FileInputStream(source);
328 inputChannel = source_fis.getChannel();
329
330 FileOutputStream dest_fos = new FileOutputStream(dest);
331 outputChannel = dest_fos.getChannel();
332
333 outputChannel.transferFrom(inputChannel, 0, inputChannel.size());
334 inputChannel.close();
335 outputChannel.close();
336 source_fis.close();
337 dest_fos.close();
338 }
339 catch(Exception e){
340 System.err.println("No restore point detected.");
341 }
342 String frameName = frameToRestore.getName();
343 String frameNameLower = frameName.toLowerCase();
344
345 // first try reading from cache
346 if (isCacheOn() && _Cache.containsKey(frameNameLower)) {
347 Logger.Log(Logger.SYSTEM, Logger.LOAD, "Clearing " + frameName
348 + " from cache.");
349 _Cache.remove(frameNameLower);
350 }
351
352 return LoadFrame(frameName, frameToRestore.getPath(), true);
353 }
354
355 public static BufferedReader LoadPublicFrame(String frameName) {
356 String fullPath = FrameIO.getFrameFullPathName(PUBLIC_PATH, frameName);
357
358 if (fullPath == null) {
359 return null;
360 }
361
362 File frameFile = new File(fullPath);
363 if (frameFile.exists() && frameFile.canRead()) {
364 try {
365 return new BufferedReader(new FileReader(frameFile));
366 } catch (FileNotFoundException e) {
367 e.printStackTrace();
368 }
369 }
370 return null;
371 }
372
373 private static Frame LoadFromDisk(String framename, String knownPath,
374 boolean ignoreAnnotations) {
375 Frame loaded = null;
376
377 if (knownPath != null) {
378 loaded = LoadKnownPath(knownPath, framename);
379 } else {
380 List<String> directoriesToSearch = FolderSettings.FrameDirs.getAbsoluteDirs();
381
382// if (AuthenticatorBrowser.Authenticated) {
383// // if we are running Expeditee Authenticated, consult user profile as location for framesets first
384// String profilePath = FrameIO.PROFILE_PATH + UserSettings.UserName.get() + File.separator;
385// directoriesToSearch.add(0, profilePath);
386// }
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 private 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) {
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 }
1419
1420 /**
1421 * Reads the INF file that corresponds to the given Frame name
1422 *
1423 * @param framename
1424 * The Frame to lookup the INF file for
1425 * @throws IOException
1426 * Any exceptions encountered by the BufferedReader used to read
1427 * the INF.
1428 */
1429 public static int ReadINF(String path, String frameset, boolean update)
1430 throws IOException {
1431 assert (!frameset.endsWith("."));
1432 try {
1433 // read INF
1434 BufferedReader reader;
1435 try {
1436 // Check on the local drive
1437 reader = new BufferedReader(new FileReader(path
1438 + frameset.toLowerCase() + File.separator
1439 + INF_FILENAME));
1440 } catch (Exception e) {
1441 reader = new BufferedReader(new FileReader(path
1442 + frameset.toLowerCase() + File.separator
1443 + frameset.toLowerCase() + ".inf"));
1444 }
1445 String inf = reader.readLine();
1446 reader.close();
1447
1448 int next = Conversion.getFrameNumber(inf);
1449 // update INF file
1450 if (update) {
1451 try {
1452 WriteINF(path, frameset, frameset + (next + 1));
1453 } catch (IOException ioe) {
1454 ioe.printStackTrace();
1455 Logger.Log(ioe);
1456 }
1457 }
1458 return next;
1459 } catch (Exception e) {
1460 }
1461
1462 // Check peers
1463 return FrameShare.getInstance().getInfNumber(path, frameset, update);
1464 }
1465
1466 /**
1467 * Writes the given String out to the INF file corresponding to the current
1468 * frameset.
1469 *
1470 * @param toWrite
1471 * The String to write to the file.
1472 * @throws IOException
1473 * Any exception encountered by the BufferedWriter.
1474 */
1475 public static void WriteINF(String path, String frameset, String frameName)
1476 throws IOException {
1477 try {
1478 assert (!frameset.endsWith("."));
1479
1480 path += frameset.toLowerCase() + File.separator + INF_FILENAME;
1481
1482 BufferedWriter writer = new BufferedWriter(new FileWriter(path));
1483 writer.write(frameName);
1484 writer.close();
1485 } catch (Exception e) {
1486
1487 }
1488 }
1489
1490 public static boolean FrameIsCached(String name) {
1491 return _Cache.containsKey(name);
1492 }
1493
1494 /**
1495 * Gets a frame from the cache.
1496 *
1497 * @param name
1498 * The frame to get from the cache
1499 *
1500 * @return The frame from cache. Null if not cached.
1501 */
1502 public static Frame FrameFromCache(String name) {
1503 return _Cache.get(name);
1504 }
1505
1506 public static String ConvertToValidFramesetName(String toValidate) {
1507 assert (toValidate != null && toValidate.length() > 0);
1508
1509 StringBuffer result = new StringBuffer();
1510
1511 if (Character.isDigit(toValidate.charAt(0))) {
1512 result.append(FRAME_NAME_LAST_CHAR);
1513 }
1514
1515 boolean capital = false;
1516 for (int i = 0; i < toValidate.length()
1517 && result.length() < MAX_NAME_LENGTH; i++) {
1518 char cur = toValidate.charAt(i);
1519
1520 // capitalize all characters after spaces
1521 if (Character.isLetterOrDigit(cur)) {
1522 if (capital) {
1523 capital = false;
1524 result.append(Character.toUpperCase(cur));
1525 } else {
1526 result.append(cur);
1527 }
1528 } else {
1529 capital = true;
1530 }
1531 }
1532 assert (result.length() > 0);
1533 int lastCharIndex = result.length() - 1;
1534 if (!Character.isLetter(result.charAt(lastCharIndex))) {
1535 if (lastCharIndex == MAX_NAME_LENGTH - 1) {
1536 result.setCharAt(lastCharIndex, FRAME_NAME_LAST_CHAR);
1537 } else {
1538 result.append(FRAME_NAME_LAST_CHAR);
1539 }
1540 }
1541
1542 assert (isValidFramesetName(result.toString()));
1543 return result.toString();
1544 }
1545
1546 public static Frame CreateNewFrame(Item linker) throws RuntimeException {
1547 String title = linker.getName();
1548
1549 String templateLink = linker.getAbsoluteLinkTemplate();
1550 String framesetLink = linker.getAbsoluteLinkFrameset();
1551 String frameset = (framesetLink != null ? framesetLink : DisplayController
1552 .getCurrentFrame().getFramesetName());
1553
1554 Frame newFrame = FrameIO.CreateFrame(frameset, title, templateLink);
1555 return newFrame;
1556 }
1557
1558 public static Frame CreateNewFrame(Item linker, OnNewFrameAction action) throws RuntimeException {
1559 Frame newFrame = FrameIO.CreateNewFrame(linker);
1560 if(action != null) {
1561 action.exec(linker, newFrame);
1562 }
1563 return newFrame;
1564 }
1565
1566 /**
1567 * Creates a new Frameset on disk, including a .0, .1, and .inf files. The
1568 * Default.0 frame is copied to make the initial .0 and .1 Frames
1569 *
1570 * @param name
1571 * The Frameset name to use
1572 * @return The name of the first Frame in the newly created Frameset (the .1
1573 * frame)
1574 */
1575 public static Frame CreateNewFrameset(String name) throws Exception {
1576 String path = DisplayController.getCurrentFrame().getPath();
1577
1578 // if current frameset is profile directory change it to framesets
1579 if (path.equals(FrameIO.PROFILE_PATH)) {
1580 path = FrameIO.FRAME_PATH;
1581 }
1582
1583 Frame newFrame = FrameIO.CreateFrameset(name, path);
1584
1585 if (newFrame == null) {
1586 // Cant create directories if the path is readonly or there is no
1587 // space available
1588 newFrame = FrameIO.CreateFrameset(name, FrameIO.FRAME_PATH);
1589 }
1590
1591 if (newFrame == null) {
1592 // TODO handle running out of disk space here
1593 }
1594
1595 return newFrame;
1596 }
1597
1598 public static Frame CreateNewGroup(String name) {
1599 try {
1600 Frame oneFrame = FrameIO.CreateFrameset(name, FrameIO.GROUP_PATH);
1601 oneFrame.setPermission(new PermissionTriple(UserAppliedPermission.full, UserAppliedPermission.none, UserAppliedPermission.none));
1602
1603 Text ownerAnnotation = oneFrame.createNewText("@Owner: " + UserSettings.UserName.get());
1604 ownerAnnotation.setPosition(100, 100);
1605 ownerAnnotation.setPermission(new PermissionTriple(UserAppliedPermission.full, UserAppliedPermission.none, UserAppliedPermission.none));
1606 Text membersAnnotation = oneFrame.createNewText("@Members: ");
1607 membersAnnotation.setPosition(100, 200);
1608
1609 FrameIO.SaveFrame(oneFrame);
1610
1611 FrameIO.LoadFrame(name + 0, FrameIO.GROUP_PATH).setPermission(new PermissionTriple(UserAppliedPermission.full, UserAppliedPermission.none, UserAppliedPermission.none));
1612
1613 return oneFrame;
1614 } catch (Exception e) {
1615 MessageBay.displayMessage("Unable to create group with name: " + name + ". See console for more details.");
1616 e.printStackTrace();
1617 return null;
1618 }
1619 }
1620
1621 /**
1622 *
1623 * @param frameset
1624 * @return
1625 */
1626 public static int getLastNumber(String frameset) { // Rob thinks it might
1627 // have been
1628 // GetHighestNumExFrame
1629 // TODO minimise the number of frames being read in!!
1630 int num = -1;
1631
1632 Frame zero = LoadFrame(frameset + "0");
1633
1634 // the frameset does not exist (or has no 0 frame)
1635 if (zero == null) {
1636 return -1;
1637 }
1638
1639 try {
1640 num = ReadINF(zero.getPath(), frameset, false);
1641 } catch (IOException e) {
1642 // TODO Auto-generated catch block
1643 // e.printStackTrace();
1644 }
1645
1646 /*
1647 * Michael doesnt think the code below is really needed... it will just
1648 * slow things down when we are reading frames over a network***** for (;
1649 * num >= 0; num--) { System.out.println("This code is loading frames to
1650 * find the highest existing frame..."); if (LoadFrame(frameset + num) !=
1651 * null) break; }
1652 */
1653
1654 return num;
1655 }
1656
1657 /**
1658 * Checks if a given frameset is accessable.
1659 *
1660 * @param framesetName
1661 * @return
1662 */
1663 public static boolean canAccessFrameset(String framesetName) {
1664 framesetName = framesetName.toLowerCase();
1665 for (String path : FolderSettings.FrameDirs.getAbsoluteDirs()) {
1666 if (canAccessFrameset(framesetName, Paths.get(path))) {
1667 return true;
1668 }
1669 }
1670 return false;
1671 }
1672
1673 public static boolean canAccessFrameset(String framesetName, Path path) {
1674 File framesetDir = path.resolve(framesetName).toFile();
1675 if (framesetDir.exists() && framesetDir.isDirectory()) {
1676 return true;
1677 } else {
1678 return false;
1679 }
1680 }
1681
1682 public static Frame CreateFrameset(String frameset, String path, boolean recreate) throws InvalidFramesetNameException, ExistingFramesetException {
1683 String conversion = frameset + " --> ";
1684
1685 if (!isValidFramesetName(frameset)) {
1686 throw new InvalidFramesetNameException(frameset);
1687 }
1688
1689 if (!recreate && FrameIO.canAccessFrameset(frameset)) {
1690 throw new ExistingFramesetException(frameset);
1691 }
1692
1693 conversion += frameset;
1694 Logger.Log(Logger.SYSTEM, Logger.NEW_FRAMESET, "Frameset Name: "
1695 + conversion);
1696 conversion = frameset;
1697
1698 /**
1699 * TODO: Update this to exclude any\all invalid filename characters
1700 */
1701 // ignore annotation character
1702 if (frameset.startsWith("@")) {
1703 frameset = frameset.substring(1);
1704 }
1705
1706 conversion += " --> " + frameset;
1707 Logger.Log(Logger.SYSTEM, Logger.NEW_FRAMESET, "Name: " + conversion);
1708
1709 // create the new Frameset directory
1710 File dir = new File(path + frameset.toLowerCase() + File.separator);
1711
1712 // If the directory doesnt already exist then create it...
1713 if (!dir.exists()) {
1714 if (!dir.mkdirs()) {
1715 /*
1716 * If the directory does not exist, but could not be created then there is something wrong.
1717 * Prior to May 2019 the only known reason for this was because the disk could be full.
1718 * Since then, we have discovered that null can occur when working with Google file stream.
1719 * A directory can return false to an existence check, but then fail to create the directory
1720 * due to it already existing because of sync issues. While we have not confirmed, this may
1721 * be the case with other network drives as well.
1722 */
1723 System.err.println("Failed to create directory for frameset: " + frameset);
1724 return null;
1725 }
1726 }
1727
1728 // create the new INF file
1729 try {
1730 WriteINF(path, frameset, frameset + '1');
1731 } catch (IOException ioe) {
1732 ioe.printStackTrace();
1733 Logger.Log(ioe);
1734 }
1735
1736 SuspendCache();
1737 // copy the default .0 and .1 files
1738 Frame base = null;
1739 try {
1740 base = LoadFrame(TemplateSettings.DefaultFrame.get());
1741 } catch (Exception e) {
1742 }
1743 // The frame may not be accessed for various reasons... in all these
1744 // cases just create a new one
1745 if (base == null) {
1746 base = new Frame();
1747 }
1748
1749 ResumeCache();
1750
1751 base.reset();
1752 base.resetDateCreated();
1753 base.setFrameset(frameset);
1754 base.setFrameNumber(0);
1755 base.setTitle(base.getFramesetName() + "0");
1756 base.setPath(path);
1757 base.change();
1758 base.setOwner(UserSettings.UserName.get());
1759 SaveFrame(base, false);
1760
1761 base.reset();
1762 base.resetDateCreated();
1763 base.setFrameNumber(1);
1764 base.setTitle(frameset);
1765 base.change();
1766 base.setOwner(UserSettings.UserName.get());
1767 SaveFrame(base, true);
1768
1769 FrameIO.setSavedProperties(base);
1770
1771 Logger.Log(Logger.SYSTEM, Logger.NEW_FRAMESET, "Created new frameset: " + frameset);
1772
1773 return base;
1774 }
1775
1776 /**
1777 * Tests if a frameset name is valid. That is it must begin and end with a
1778 * letter and contain only letters and digits in between.
1779 *
1780 * @param frameset
1781 * the name to be tested
1782 * @return true if the frameset name is valid
1783 */
1784 public static boolean isValidFramesetName(String frameset) {
1785 if (frameset == null) {
1786 return false;
1787 }
1788
1789 int nameLength = frameset.length();
1790 if (frameset.length() <= 0 || nameLength > MAX_NAME_LENGTH) {
1791 return false;
1792 }
1793
1794 int lastCharIndex = nameLength - 1;
1795
1796 if (!Character.isLetter(frameset.charAt(0))
1797 || !Character.isLetter(frameset.charAt(lastCharIndex))) {
1798 return false;
1799 }
1800
1801 for (int i = 1; i < lastCharIndex; i++) {
1802 if (!isValidFrameNameChar(frameset.charAt(i))) {
1803 return false;
1804 }
1805 }
1806 return true;
1807 }
1808
1809 public static boolean deleteFrameset(String framesetName) {
1810 return moveFrameset(framesetName, FrameIO.TRASH_PATH, true);
1811 }
1812
1813 public static boolean moveFrameset(String framesetName, String destinationFolder, boolean override) {
1814 if (!FrameIO.canAccessFrameset(framesetName)) {
1815 return false;
1816 }
1817 // Clear the cache
1818 _Cache.clear();
1819
1820 // Search all the available directories for the directory
1821 for (String path : FolderSettings.FrameDirs.getAbsoluteDirs()) {
1822 return moveFrameset(framesetName, path, destinationFolder, override);
1823 }
1824 return false;
1825 }
1826
1827 public static boolean moveFrameset(String framesetName, String path, String destinationFolder, boolean override) {
1828 String source = path + framesetName.toLowerCase() + File.separator;
1829 File framesetDirectory = new File(source);
1830 // Once we have found the directory move it
1831 if (framesetDirectory.exists()) {
1832 String destPath = destinationFolder
1833 + framesetName.toLowerCase();
1834 int copyNumber = 1;
1835 File dest = new File(destPath + File.separator);
1836 // Create the destination folder if it doesnt already exist
1837 if (!dest.getParentFile().exists()) {
1838 dest.mkdirs();
1839 }
1840 // If a frameset with the same name is already in the
1841 // destination add
1842 // a number to the end
1843 while (dest.exists() && !override) {
1844 dest = new File(destPath + ++copyNumber + File.separator);
1845 }
1846 try {
1847 moveFileTree(framesetDirectory.toPath(), dest.toPath());
1848 } catch (IOException e) {
1849 e.printStackTrace();
1850 return false;
1851 }
1852
1853 for (File f : framesetDirectory.listFiles()) {
1854 if (!f.delete()) {
1855 return false;
1856 }
1857 }
1858 if (!framesetDirectory.delete()) {
1859 return false;
1860 }
1861 return true;
1862 } else {
1863 return false;
1864 }
1865 }
1866
1867 public static boolean CopyFrameset(String framesetToCopy,
1868 String copiedFrameset) throws Exception {
1869 if (!FrameIO.canAccessFrameset(framesetToCopy)) {
1870 return false;
1871 }
1872 if (FrameIO.canAccessFrameset(copiedFrameset)) {
1873 return false;
1874 }
1875 // search through all the directories to find the frameset we are
1876 // copying
1877 for (String path : FolderSettings.FrameDirs.getAbsoluteDirs()) {
1878 String source = path + framesetToCopy.toLowerCase()
1879 + File.separator;
1880 File framesetDirectory = new File(source);
1881 if (framesetDirectory.exists()) {
1882 // copy the frameset
1883 File copyFramesetDirectory = new File(path
1884 + copiedFrameset.toLowerCase() + File.separator);
1885 if (!copyFramesetDirectory.mkdirs()) {
1886 return false;
1887 }
1888 // copy each of the frames
1889 for (File f : framesetDirectory.listFiles()) {
1890 // Ignore hidden files
1891 if (f.getName().charAt(0) == '.') {
1892 continue;
1893 }
1894 String copyPath = copyFramesetDirectory.getAbsolutePath()
1895 + File.separator + f.getName();
1896 FrameIO.copyFile(f.getAbsolutePath(), copyPath);
1897 }
1898 return true;
1899 }
1900 }
1901 return false;
1902 }
1903
1904 /**
1905 * Copies a file from one location to another.
1906 *
1907 * @param existingFile
1908 * @param newFileName
1909 * @throws Exception
1910 */
1911 public static void copyFile(String existingFile, String newFileName)
1912 throws IOException {
1913 FileInputStream is = new FileInputStream(existingFile);
1914 FileOutputStream os = new FileOutputStream(newFileName, false);
1915 int data;
1916 while ((data = is.read()) != -1) {
1917 os.write(data);
1918 }
1919 os.flush();
1920 os.close();
1921 is.close();
1922 }
1923
1924 /**
1925 * Saves a frame regardless of whether or not the frame is marked as having
1926 * been changed.
1927 *
1928 * @param frame
1929 * the frame to save
1930 * @return the contents of the frame or null if it could not be saved
1931 */
1932 public static String ForceSaveFrame(Frame frame) {
1933 frame.change();
1934 return SaveFrame(frame, false);
1935 }
1936
1937 public static boolean isValidLink(String frameName) {
1938 return frameName == null || isPositiveInteger(frameName)
1939 || isValidFrameName(frameName);
1940 }
1941
1942 public static void SavePublicFrame(String peerName, String frameName,
1943 int version, BufferedReader packetContents) {
1944 // TODO handle versioning - add version to the header
1945 // Remote user uploads version based on an old version
1946
1947 // Remove it from the cache so that next time it is loaded we get the up
1948 // todate version
1949 _Cache.remove(frameName.toLowerCase());
1950
1951 // Save to file
1952 String filename = PUBLIC_PATH + Conversion.getFramesetName(frameName)
1953 + File.separator + Conversion.getFrameNumber(frameName)
1954 + ExpReader.EXTENTION;
1955
1956 File file = new File(filename);
1957 // Ensure the file exists
1958 if (file.exists()) {
1959 // Check the versions
1960 int savedVersion = ExpReader.getVersion(filename);
1961
1962 if (savedVersion > version) {
1963 // remove this frame from the cache if it is there
1964 // This will make sure links to the original are set correctly
1965 // _Cache.remove(frameName.toLowerCase());
1966
1967 int nextNum = 0;
1968 try {
1969 nextNum = ReadINF(PUBLIC_PATH, Conversion
1970 .getFramesetName(frameName), false) + 1;
1971 } catch (IOException e) {
1972 e.printStackTrace();
1973 }
1974
1975 String newName = Conversion.getFramesetName(frameName)
1976 + nextNum;
1977 filename = PUBLIC_PATH + Conversion.getFramesetName(frameName)
1978 + File.separator + nextNum + ExpReader.EXTENTION;
1979
1980 // Show the messages alerting the user
1981 Text originalMessage = new Text(-1);
1982 originalMessage.setColor(MessageBay.ERROR_COLOR);
1983 originalMessage.setText(frameName + " was edited by "
1984 + peerName);
1985 originalMessage.setLink(frameName);
1986 Text yourMessage = new Text(-1);
1987 yourMessage.setColor(MessageBay.ERROR_COLOR);
1988 yourMessage.setText("Their version was renamed " + newName);
1989 yourMessage.setLink(newName);
1990 MessageBay.displayMessage(originalMessage);
1991 MessageBay.displayMessage(yourMessage);
1992
1993 Frame editedFrame = FrameIO.LoadFrame(frameName);
1994
1995 FrameShare.getInstance().sendMessage(
1996 frameName + " was recently edited by "
1997 + editedFrame.getLastModifyUser(), peerName);
1998 FrameShare.getInstance().sendMessage(
1999 "Your version was renamed " + newName, peerName);
2000 }
2001 }
2002
2003 // Save the new version
2004 try {
2005 // FileWriter fw = new FileWriter(file);
2006
2007 // Open an Output Stream Writer to set encoding
2008 OutputStream fout = new FileOutputStream(file);
2009 OutputStream bout = new BufferedOutputStream(fout);
2010 Writer fw = new OutputStreamWriter(bout, "UTF-8");
2011
2012 String nextLine = null;
2013 while ((nextLine = packetContents.readLine()) != null) {
2014 fw.write(nextLine + '\n');
2015 }
2016 fw.flush();
2017 fw.close();
2018 MessageBay.displayMessage("Saved remote frame: " + frameName);
2019 } catch (IOException e) {
2020 MessageBay.errorMessage("Error remote saving " + frameName + ": "
2021 + e.getMessage());
2022 e.printStackTrace();
2023 }
2024 }
2025
2026 public static void setSavedProperties(Frame toSave) {
2027 toSave.setLastModifyDate(Formatter.getDateTime(), System.currentTimeMillis());
2028 toSave.setLastModifyUser(UserSettings.UserName.get());
2029 toSave.setVersion(toSave.getVersion() + 1);
2030 Time darkTime = new Time(SessionStats.getFrameDarkTime().getTime()
2031 + toSave.getDarkTime().getTime());
2032 Time activeTime = new Time(SessionStats.getFrameActiveTime().getTime()
2033 + toSave.getActiveTime().getTime());
2034 toSave.setDarkTime(darkTime);
2035 toSave.setActiveTime(activeTime);
2036 }
2037
2038 public static boolean personalResourcesExist(String username) {
2039 Path personalResources = Paths.get(FrameIO.PARENT_FOLDER).resolve("resources-" + username);
2040 File personalResourcesFile = personalResources.toFile();
2041 boolean directoryExists = personalResourcesFile.exists() && personalResourcesFile.isDirectory();
2042 return directoryExists;
2043 }
2044
2045 public static Path setupPersonalResources(String username) {
2046 Path personalResources = Paths.get(FrameIO.PARENT_FOLDER).resolve("resources-" + username);
2047 personalResources.toFile().mkdir();
2048 File[] globalResourcesToCopy = Paths.get(FrameIO.RESOURCES_PRIVATE_PATH).toFile().listFiles();
2049
2050 try {
2051 for (File toCopy: globalResourcesToCopy) {
2052 Path p = Paths.get(toCopy.getAbsolutePath());
2053 if (!p.getFileName().toString().equals(".res") && !p.getFileName().toString().equals("about")) {
2054 moveFileTree(p.toAbsolutePath(), personalResources.resolve(p.getFileName()));
2055 }
2056 }
2057 } catch (IOException e) {
2058 e.printStackTrace();
2059 personalResources = null;
2060 }
2061
2062 return personalResources;
2063 }
2064
2065 public static void migrateFrame(Frame toMigrate, Path destinationDirectory) {
2066 Path source = Paths.get(toMigrate.getFramePathReal());
2067 String destination = source.relativize(destinationDirectory).toString().substring(3).replace(File.separator, "/");
2068 try {
2069 Files.move(source, destinationDirectory);
2070 } catch (IOException e) {
2071 System.err.println("FrameIO::migrateFrame: failed to migrate from to new location. Message: " + e.getMessage());
2072 return;
2073 }
2074 try {
2075 FileWriter out = new FileWriter(source.toFile());
2076 out.write("REDIRECT:" + destination);
2077 out.flush();
2078 out.close();
2079 } catch (IOException e) {
2080 System.err.println("FrameIO::migrateFrame: failed to update file [" + source + "] to redirect to [" + destination + "] following migration. Message: " + e.getMessage());
2081 }
2082 }
2083
2084 private static void moveFileTree(Path source, Path target) throws IOException {
2085 if (source.toFile().isDirectory()) {
2086 if (!target.toFile().exists()) {
2087 Files.copy(source, target);
2088 }
2089 File[] files = source.toFile().listFiles();
2090 for (File file: files) {
2091 Path asPath = Paths.get(file.getAbsolutePath());
2092 moveFileTree(asPath, target.resolve(asPath.getFileName()));
2093 }
2094 } else {
2095 Files.copy(source, target, StandardCopyOption.REPLACE_EXISTING);
2096 }
2097 }
2098}
Note: See TracBrowser for help on using the repository browser.