OpenConcerto

Dépôt officiel du code source de l'ERP OpenConcerto
sonarqube

svn://code.openconcerto.org/openconcerto

Rev

Rev 142 | Go to most recent revision | Details | Compare with Previous | Last modification | View Log | RSS feed

Rev Author Line No. Line
142 ilm 1
/*
2
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
3
 *
4
 * Copyright 2011 OpenConcerto, by ILM Informatique. All rights reserved.
5
 *
6
 * The contents of this file are subject to the terms of the GNU General Public License Version 3
7
 * only ("GPL"). You may not use this file except in compliance with the License. You can obtain a
8
 * copy of the License at http://www.gnu.org/licenses/gpl-3.0.html See the License for the specific
9
 * language governing permissions and limitations under the License.
10
 *
11
 * When distributing the software, include this License Header Notice in each file.
12
 */
13
 
14
 package org.openconcerto.utils;
15
 
16
import java.io.File;
17
import java.io.IOException;
144 ilm 18
import java.nio.file.Files;
19
import java.nio.file.attribute.PosixFilePermissions;
142 ilm 20
 
21
/**
22
 * A set of base directories.
23
 *
24
 * @author Sylvain CUAZ
25
 */
26
public abstract class BaseDirs {
27
 
28
    static private final String DATA = "Data";
29
    static private final String PREFERENCES = "Preferences";
30
    static private final String CACHES = "Caches";
31
 
32
    // https://specifications.freedesktop.org/basedir-spec/basedir-spec-latest.html
33
    static public final class XDG extends BaseDirs {
34
        protected XDG(final ProductInfo info, final String subdir) {
35
            super(info, subdir);
36
        }
37
 
38
        @Override
39
        protected File _getAppDataFolder() {
40
            /*
41
             * $XDG_DATA_HOME defines the base directory relative to which user specific data files
42
             * should be stored. If $XDG_DATA_HOME is either not set or empty, a default equal to
43
             * $HOME/.local/share should be used.
44
             */
45
            return new File(StringUtils.coalesce(System.getenv("XDG_DATA_HOME"), System.getenv("HOME") + "/.local/share"), this.getAppID());
46
        }
47
 
48
        @Override
49
        protected File _getPreferencesFolder() {
50
            /*
51
             * $XDG_CONFIG_HOME defines the base directory relative to which user specific
52
             * configuration files should be stored. If $XDG_CONFIG_HOME is either not set or empty,
53
             * a default equal to $HOME/.config should be used.
54
             */
55
            return new File(StringUtils.coalesce(System.getenv("XDG_CONFIG_HOME"), System.getenv("HOME") + "/.config"), this.getAppID());
56
        }
57
 
58
        @Override
59
        protected File _getCacheFolder() {
60
            /*
61
             * $XDG_CACHE_HOME defines the base directory relative to which user specific
62
             * non-essential data files should be stored. If $XDG_CACHE_HOME is either not set or
63
             * empty, a default equal to $HOME/.cache should be used.
64
             */
65
            return new File(StringUtils.coalesce(System.getenv("XDG_CACHE_HOME"), System.getenv("HOME") + "/.cache"), this.getAppID());
66
        }
67
    }
68
 
69
    static public final class Unknown extends BaseDirs {
70
 
71
        protected Unknown(final ProductInfo info, final String subdir) {
72
            super(info, subdir);
73
        }
74
 
75
    }
76
 
77
    static public final class Windows extends BaseDirs {
78
        private final String path;
79
 
80
        protected Windows(final ProductInfo info, final String subdir) {
81
            super(info, subdir);
82
            final String orgID = info.getOrganizationName() == null ? null : FileUtils.sanitize(info.getOrganizationName());
83
            final String appID = this.getAppName();
84
            // handle missing org and avoid OpenConcerto/OpenConcerto
85
            this.path = orgID == null || orgID.equals(appID) ? appID : orgID + File.separatorChar + appID;
86
            // ProductInfo test emptiness
87
            assert this.path.charAt(0) != File.separatorChar && this.path.charAt(this.path.length() - 1) != File.separatorChar : "Separator not in between : " + this.path;
88
        }
89
 
90
        protected final String getPath() {
91
            return this.path;
92
        }
93
 
94
        @Override
95
        protected File _getAppDataFolder() {
96
            // do not use LOCALAPPDATA as the user needs its data synchronised
97
            return new File(System.getenv("APPDATA"), this.getPath() + File.separatorChar + DATA);
98
        }
99
 
100
        @Override
101
        protected File _getPreferencesFolder() {
102
            // do not use LOCALAPPDATA as configuration should be small enough to be synchronised on
103
            // the network
104
            return new File(System.getenv("APPDATA"), this.getPath() + File.separatorChar + PREFERENCES);
105
        }
106
 
107
        @Override
108
        protected File _getCacheFolder() {
109
            // use LOCALAPPDATA as caches can be quite big and don't need to be synchronised
110
            return new File(System.getenv("LOCALAPPDATA"), this.getPath() + File.separatorChar + CACHES);
111
        }
112
    }
113
 
114
    // https://developer.apple.com/library/mac/qa/qa1170/_index.html
115
    static public final class Mac extends BaseDirs {
116
 
117
        protected Mac(final ProductInfo info, final String subdir) {
118
            super(info, subdir);
119
        }
120
 
121
        @Override
122
        protected File _getAppDataFolder() {
123
            // NOTE : "Application Support" directory is reserved for non-essential application
124
            // resources
125
            return new File(System.getProperty("user.home") + "/Library/" + this.getAppName());
126
        }
127
 
128
        @Override
129
        protected File _getPreferencesFolder() {
130
            return new File(System.getProperty("user.home") + "/Library/Preferences/" + this.getAppFullID());
131
        }
132
 
133
        @Override
134
        protected File _getCacheFolder() {
135
            return new File(System.getProperty("user.home") + "/Library/Caches/" + this.getAppFullID());
136
        }
137
    }
138
 
139
    static public final class Portable extends BaseDirs {
140
 
141
        private final File rootDir;
142
 
143
        protected Portable(final File rootDir, final ProductInfo info, final String subdir) {
144
            super(info, subdir);
145
            this.rootDir = rootDir;
146
        }
147
 
148
        public final File getRootDir() {
149
            return this.rootDir;
150
        }
151
 
152
        @Override
153
        protected File _getAppDataFolder() {
154
            return new File(this.getRootDir(), DATA);
155
        }
156
 
157
        @Override
158
        protected File _getPreferencesFolder() {
159
            return new File(this.getRootDir(), PREFERENCES);
160
        }
161
 
162
        @Override
163
        protected File _getCacheFolder() {
164
            return new File(this.getRootDir(), CACHES);
165
        }
166
    }
167
 
168
    public static final BaseDirs createPortable(final File rootDir, final ProductInfo info, final String subdir) {
169
        return new Portable(rootDir, info, subdir);
170
    }
171
 
172
    public static final BaseDirs create(final ProductInfo info) {
173
        return create(info, null);
174
    }
175
 
176
    public static final BaseDirs create(final ProductInfo info, final String subdir) {
177
        final OSFamily os = OSFamily.getInstance();
178
        if (os == OSFamily.Windows)
179
            return new Windows(info, subdir);
180
        else if (os == OSFamily.Mac)
181
            return new Mac(info, subdir);
182
        else if (os instanceof OSFamily.Unix)
183
            return new XDG(info, subdir);
184
        else
185
            return new Unknown(info, subdir);
186
    }
187
 
188
    private final ProductInfo info;
189
    private final String subdir;
190
 
191
    protected BaseDirs(final ProductInfo info, final String subdir) {
192
        this.info = info;
193
        this.subdir = subdir == null ? null : FileUtils.sanitize(subdir);
194
    }
195
 
196
    // should use other methods to avoid invalid characters
197
    private final ProductInfo getInfo() {
198
        return this.info;
199
    }
200
 
201
    protected final String getAppName() {
202
        return FileUtils.sanitize(this.getInfo().getName());
203
    }
204
 
205
    protected final String getAppID() {
206
        return this.getInfo().getID();
207
    }
208
 
209
    protected final String getAppFullID() {
210
        final String res = this.getInfo().getFullID();
211
        return res != null ? res : this.getAppID();
212
    }
213
 
214
    protected File getFolderToWrite(final File dir) throws IOException {
215
        if (dir.isDirectory() && dir.canWrite())
216
            return dir;
217
        if (dir.exists())
218
            throw new IOException((dir.isDirectory() ? "Not writable: " : "Not a directory: ") + dir);
144 ilm 219
        // create with 0700 mode (from § Referencing this specification)
220
        final String perms = "rwx------";
221
        try {
222
            Files.createDirectories(dir.toPath(), PosixFilePermissions.asFileAttribute(PosixFilePermissions.fromString(perms)));
223
        } catch (UnsupportedOperationException e) {
224
            // e.g. this is Windows
225
            Files.createDirectories(dir.toPath());
226
            FileUtils.setFilePermissionsFromPOSIX(dir, perms);
227
        }
142 ilm 228
        return dir;
229
    }
230
 
231
    protected final File getSubDir(final File dir) {
232
        return this.subdir == null ? dir : new File(dir, this.subdir);
233
    }
234
 
235
    protected File _getAppDataFolder() {
236
        return new File(System.getProperty("user.home"), "." + this.getAppFullID());
237
    }
238
 
239
    // where to write user-hidden data files (e.g. mbox files, DB files)
240
    public final File getAppDataFolder() {
241
        return getSubDir(_getAppDataFolder());
242
    }
243
 
244
    public final File getAppDataFolderToWrite() throws IOException {
245
        return getFolderToWrite(this.getAppDataFolder());
246
    }
247
 
248
    protected File _getPreferencesFolder() {
249
        return this.getAppDataFolder();
250
    }
251
 
252
    // where to write configuration
253
    public final File getPreferencesFolder() {
254
        return getSubDir(_getPreferencesFolder());
255
    }
256
 
257
    public final File getPreferencesFolderToWrite() throws IOException {
258
        return getFolderToWrite(this.getPreferencesFolder());
259
    }
260
 
261
    protected File _getCacheFolder() {
262
        return new File(System.getProperty("java.io.tmpdir"), this.getAppFullID());
263
    }
264
 
265
    // where to write data that can be re-created
266
    public final File getCacheFolder() {
267
        return getSubDir(_getCacheFolder());
268
    }
269
 
270
    public final File getCacheFolderToWrite() throws IOException {
271
        return getFolderToWrite(this.getCacheFolder());
272
    }
273
 
274
    @Override
275
    public String toString() {
276
        return BaseDirs.class.getSimpleName() + " " + this.getClass().getSimpleName();
277
    }
278
 
144 ilm 279
    public static void main(String[] args) throws IOException {
142 ilm 280
        final String appName = args.length > 0 ? args[0] : "fooApp";
281
        final String companyName = args.length > 1 ? args[1] : "acme";
282
        final String subdir = System.getProperty("subdir");
283
        final BaseDirs instance = create(new ProductInfo(CollectionUtils.createMap(ProductInfo.ORGANIZATION_NAME, companyName, ProductInfo.NAME, appName)), subdir);
284
        System.out.println(instance);
285
        System.out.println("app data : " + instance.getAppDataFolder());
286
        System.out.println("preferences : " + instance.getPreferencesFolder());
287
        System.out.println("cache : " + instance.getCacheFolder());
144 ilm 288
        // test creation and permission
289
        if (Boolean.getBoolean("createCacheDir")) {
290
            final File createdCache = instance.getCacheFolderToWrite();
291
            System.out.println("cache dir created : " + createdCache);
292
            if (Boolean.getBoolean("deleteCacheDir")) {
293
                Files.delete(createdCache.toPath());
294
                System.out.println("cache dir deleted");
295
            }
296
        }
142 ilm 297
    }
298
}