OpenConcerto

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

svn://code.openconcerto.org/openconcerto

Rev

Rev 180 | Details | Compare with Previous | Last modification | View Log | RSS feed

Rev Author Line No. Line
17 ilm 1
/*
2
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
3
 *
182 ilm 4
 * Copyright 2011-2019 OpenConcerto, by ILM Informatique. All rights reserved.
17 ilm 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
 
80 ilm 16
import org.openconcerto.utils.OSFamily.Unix;
17
 
17 ilm 18
import java.io.ByteArrayOutputStream;
19
import java.io.File;
20
import java.io.IOException;
142 ilm 21
import java.io.InputStream;
17 ilm 22
import java.lang.reflect.Method;
93 ilm 23
import java.nio.charset.Charset;
180 ilm 24
import java.util.concurrent.FutureTask;
25
import java.util.concurrent.RunnableFuture;
142 ilm 26
import java.util.logging.Level;
17 ilm 27
import java.util.regex.Matcher;
28
import java.util.regex.Pattern;
29
 
180 ilm 30
import javax.swing.SwingUtilities;
17 ilm 31
import javax.swing.filechooser.FileSystemView;
142 ilm 32
import javax.xml.parsers.DocumentBuilder;
33
import javax.xml.parsers.DocumentBuilderFactory;
17 ilm 34
 
142 ilm 35
import org.w3c.dom.Element;
36
import org.w3c.dom.NodeList;
37
 
180 ilm 38
import net.jcip.annotations.GuardedBy;
39
import net.jcip.annotations.ThreadSafe;
40
 
17 ilm 41
/**
42
 * A desktop environment like Gnome or MacOS.
43
 *
44
 * @author Sylvain CUAZ
45
 * @see #getDE()
46
 */
180 ilm 47
@ThreadSafe
17 ilm 48
public abstract class DesktopEnvironment {
49
 
50
    static public final class Gnome extends DesktopEnvironment {
51
 
142 ilm 52
        static private final String getTextContent(final Element parentElem, final String childName) {
53
            final NodeList children = parentElem.getElementsByTagName(childName);
54
            if (children.getLength() != 1)
55
                throw new IllegalStateException("Not one child " + childName + " in " + parentElem);
56
            return children.item(0).getTextContent();
57
        }
58
 
59
        private final String name;
60
 
61
        public Gnome(final String name) {
62
            this.name = name;
63
        }
64
 
17 ilm 65
        @Override
66
        protected String findVersion() {
67
            try {
142 ilm 68
                if (this.name.equals("gnome")) {
69
                    // e.g. GNOME gnome-about 2.24.1
70
                    final String line = cmdSubstitution(Runtime.getRuntime().exec(new String[] { "gnome-about", "--version" }));
71
                    final String[] words = line.split(" ");
72
                    return words[words.length - 1];
73
                } else if (this.name.equals("gnome3")) {
74
                    final DocumentBuilder builder = DocumentBuilderFactory.newInstance().newDocumentBuilder();
75
                    // <gnome-version>
76
                    // <platform>3</platform>
77
                    // <minor>18</minor>
78
                    // <micro>2</micro>
79
                    // </gnome-version>
80
                    final Element root = builder.parse(new File("/usr/share/gnome/gnome-version.xml")).getDocumentElement();
81
                    return getTextContent(root, "platform") + '.' + getTextContent(root, "minor") + '.' + getTextContent(root, "micro");
82
                } else {
83
                    throw new IllegalStateException("unknown name : " + this.name);
84
                }
85
            } catch (Exception e) {
86
                e.printStackTrace();
87
                return null;
88
            }
89
        }
90
    }
91
 
92
    static public final class MATE extends DesktopEnvironment {
93
 
94
        @Override
95
        protected String findVersion() {
96
            try {
17 ilm 97
                // e.g. GNOME gnome-about 2.24.1
142 ilm 98
                final String line = cmdSubstitution(Runtime.getRuntime().exec(new String[] { "mate-about", "--version" }));
17 ilm 99
                final String[] words = line.split(" ");
100
                return words[words.length - 1];
101
            } catch (Exception e) {
102
                e.printStackTrace();
103
                return null;
104
            }
105
        }
106
    }
107
 
108
    static public final class KDE extends DesktopEnvironment {
109
 
110
        private static final Pattern versionPattern = Pattern.compile("^KDE: (.*)$", Pattern.MULTILINE);
111
 
112
        @Override
113
        protected String findVersion() {
114
            try {
115
                // Qt: 3.3.8b
116
                // KDE: 3.5.10
117
                // kde-config: 1.0
118
                final String line = cmdSubstitution(Runtime.getRuntime().exec(new String[] { "kde-config", "--version" }));
119
                final Matcher matcher = versionPattern.matcher(line);
120
                matcher.find();
121
                return matcher.group(1);
122
            } catch (Exception e) {
123
                e.printStackTrace();
124
                return null;
125
            }
126
        }
127
    }
128
 
129
    static public final class XFCE extends DesktopEnvironment {
142 ilm 130
        // xfce4-about 4.11.1 (Xfce 4.10)
131
        // Copyright (c) 2008-2011
132
        private static final Pattern versionPattern = Pattern.compile("^xfce4-about.+\\(\\p{Alnum}+\\p{Blank}+(.+)\\)$", Pattern.MULTILINE);
17 ilm 133
 
134
        @Override
135
        protected String findVersion() {
142 ilm 136
            try {
137
                final String line = cmdSubstitution(Runtime.getRuntime().exec(new String[] { "xfce4-about", "--version" }));
138
                final Matcher matcher = versionPattern.matcher(line);
139
                matcher.find();
140
                return matcher.group(1);
141
            } catch (Exception e) {
142
                e.printStackTrace();
143
                return null;
144
            }
17 ilm 145
        }
146
    }
147
 
148
    static public final class Unknown extends DesktopEnvironment {
149
        @Override
150
        protected String findVersion() {
151
            return "";
152
        }
153
    }
154
 
155
    static private class DEisOS extends DesktopEnvironment {
156
        @Override
157
        protected String findVersion() {
158
            return System.getProperty("os.version");
159
        }
160
    }
161
 
162
    static public final class Windows extends DEisOS {
61 ilm 163
        // e.g. bash.exe
164
        public String quoteParamForGCC(String s) {
28 ilm 165
            return StringUtils.doubleQuote(s);
166
        }
17 ilm 167
    }
168
 
169
    static public final class Mac extends DEisOS {
170
 
171
        // From CarbonCore/Folders.h
180 ilm 172
        public static final String kDocumentsDirectory = "docs";
173
        public static final String kPreferencesDirectory = "pref";
17 ilm 174
        private static Class<?> FileManagerClass;
175
        private static Short kUserDomain;
176
        private static Method OSTypeToInt;
177
 
178
        private static Class<?> getFileManagerClass() {
179
            if (FileManagerClass == null) {
180
                try {
181
                    FileManagerClass = Class.forName("com.apple.eio.FileManager");
182
                    OSTypeToInt = FileManagerClass.getMethod("OSTypeToInt", String.class);
183
                    kUserDomain = (Short) FileManagerClass.getField("kUserDomain").get(null);
184
                } catch (RuntimeException e) {
185
                    throw e;
186
                } catch (Exception e) {
187
                    throw new IllegalStateException(e);
188
                }
189
            }
190
            return FileManagerClass;
191
        }
192
 
193
        @Override
194
        public File getDocumentsFolder() {
180 ilm 195
            return new File(System.getProperty("user.home"), "Documents/");
17 ilm 196
        }
197
 
180 ilm 198
        // There was a warning until JRE v16, now "--add-exports
199
        // java.desktop/com.apple.eio=ALL-UNNAMED" is needed. Further this needs the EDT which is
200
        // undesirable just to get some paths.
201
        // https://bugs.openjdk.java.net/browse/JDK-8187981
202
        @Deprecated
17 ilm 203
        public File getFolder(String type) {
204
            try {
205
                final Method findFolder = getFileManagerClass().getMethod("findFolder", Short.TYPE, Integer.TYPE);
180 ilm 206
                final RunnableFuture<String> f = new FutureTask<>(() -> (String) findFolder.invoke(null, kUserDomain, OSTypeToInt.invoke(null, type)));
207
                // EDT needed at least for JRE v14-16 on macOS 10.15, otherwise the VM crashes :
208
                // Problematic frame: __NSAssertMainEventQueueIsCurrentEventQueue_block_invoke or
209
                // "The current event queue and the main event queue are not the same. This is
210
                // probably because _TSGetMainThread was called for the first time off the main
211
                // thread."
212
                if (SwingUtilities.isEventDispatchThread())
213
                    f.run();
214
                else
215
                    SwingUtilities.invokeLater(f);
216
                final String path = f.get();
17 ilm 217
                return new File(path);
218
            } catch (RuntimeException e) {
219
                throw e;
220
            } catch (Exception e) {
221
                throw new IllegalStateException(e);
222
            }
223
        }
224
 
61 ilm 225
        public File getAppDir(final String bundleID) throws IOException {
226
            // we used to ask for the URL of the application file but since 10.7 it returns a
227
            // file reference URL like "file:///.file/id=6723689.35865"
142 ilm 228
            final ProcessBuilder processBuilder = new ProcessBuilder("osascript", "-e",
229
                    "tell application id \"com.apple.Finder\" to POSIX path of (application file id \"" + bundleID + "\" as string)");
61 ilm 230
            // if not found prints nothing to out and a cryptic error to the standard error stream
231
            final String dir = cmdSubstitution(processBuilder.start()).trim();
232
            return dir.length() == 0 ? null : new File(dir);
233
        }
17 ilm 234
    }
235
 
236
    /**
237
     * Execute the passed command and test its return code.
238
     *
239
     * @param command the command to {@link Runtime#exec(String[]) execute}.
240
     * @return <code>false</code> if the {@link Process#waitFor() return code} is not 0 or an
241
     *         exception is thrown.
242
     * @throws RTInterruptedException if this is interrupted while waiting.
243
     */
244
    public static final boolean test(final String... command) throws RTInterruptedException {
245
        try {
246
            return Runtime.getRuntime().exec(command).waitFor() == 0;
247
        } catch (InterruptedException e) {
248
            throw new RTInterruptedException(e);
249
        } catch (IOException e) {
250
            Log.get().finer(e.getLocalizedMessage());
251
            return false;
252
        }
253
    }
254
 
255
    public final static String cmdSubstitution(Process p) throws IOException {
93 ilm 256
        return cmdSubstitution(p, null);
257
    }
258
 
259
    public final static String cmdSubstitution(final Process p, final Charset encoding) throws IOException {
17 ilm 260
        final ByteArrayOutputStream out = new ByteArrayOutputStream(100 * 1024);
144 ilm 261
        // some programs won't write anything until they read everything (e.g. powershell.exe)
262
        p.getOutputStream().close();
93 ilm 263
        p.getErrorStream().close();
17 ilm 264
        StreamUtils.copy(p.getInputStream(), out);
93 ilm 265
        p.getInputStream().close();
266
        return encoding == null ? out.toString() : out.toString(encoding.name());
17 ilm 267
    }
268
 
142 ilm 269
    private static final String detectXDG() {
270
        String res = null;
271
        InputStream scriptIns = null;
272
        try {
273
            scriptIns = DesktopEnvironment.class.getResourceAsStream("DesktopEnvironmentXDG.sh");
274
            final Process ps = new ProcessBuilder("sh").start();
275
            ps.getErrorStream().close();
276
            StreamUtils.copy(scriptIns, ps.getOutputStream());
277
            ps.getOutputStream().close();
278
            res = FileUtils.readUTF8(ps.getInputStream()).trim();
279
            ps.getInputStream().close();
280
            if (ps.waitFor() != 0)
281
                throw new IllegalStateException("Not OK : " + ps.exitValue());
282
        } catch (Exception e) {
283
            Log.get().fine(e.getLocalizedMessage());
284
        } finally {
285
            if (scriptIns != null) {
286
                try {
287
                    scriptIns.close();
288
                } catch (IOException e) {
289
                    e.printStackTrace();
290
                }
291
            }
292
        }
293
        return res;
294
    }
295
 
17 ilm 296
    private static final DesktopEnvironment detectDE() {
80 ilm 297
        final OSFamily os = OSFamily.getInstance();
298
        if (os == OSFamily.Windows) {
17 ilm 299
            return new Windows();
80 ilm 300
        } else if (os == OSFamily.Mac) {
17 ilm 301
            return new Mac();
80 ilm 302
        } else if (os instanceof Unix) {
142 ilm 303
            final String de = detectXDG();
304
            if (de.equals("xfce"))
305
                return new XFCE();
306
            else if (de.equals("mate"))
307
                return new MATE();
308
            else if (de.equals("kde"))
17 ilm 309
                return new KDE();
142 ilm 310
            else if (de.startsWith("gnome"))
311
                return new Gnome(de);
17 ilm 312
        }
313
        return new Unknown();
314
    }
315
 
180 ilm 316
    @GuardedBy("DesktopEnvironment.class")
17 ilm 317
    private static DesktopEnvironment DE = null;
318
 
180 ilm 319
    public synchronized static final DesktopEnvironment getDE() {
17 ilm 320
        if (DE == null) {
321
            DE = detectDE();
322
        }
323
        return DE;
324
    }
325
 
180 ilm 326
    public synchronized static final void resetDE() {
17 ilm 327
        DE = null;
328
    }
329
 
180 ilm 330
    @GuardedBy("this")
17 ilm 331
    private String version;
332
 
333
    private DesktopEnvironment() {
334
        this.version = null;
335
    }
336
 
337
    protected abstract String findVersion();
338
 
180 ilm 339
    public synchronized final String getVersion() {
17 ilm 340
        if (this.version == null)
341
            this.version = this.findVersion();
342
        return this.version;
343
    }
344
 
142 ilm 345
    // where to write user-visible files (e.g. spreadsheets, exports)
17 ilm 346
    public File getDocumentsFolder() {
347
        return FileSystemView.getFileSystemView().getDefaultDirectory();
348
    }
349
 
28 ilm 350
    // on some systems arguments are not passed correctly by ProcessBuilder
182 ilm 351
    public final String quoteParamForExec(String s) {
352
        return Platform.getInstance().getProcessArg(s);
28 ilm 353
    }
354
 
17 ilm 355
    @Override
356
    public String toString() {
357
        return "DesktopEnvironment " + this.getClass().getSimpleName();
358
    }
359
 
360
    public static void main(String[] args) {
142 ilm 361
        // removes default
362
        LogUtils.rmRootHandlers();
363
        // add console handler
364
        LogUtils.setUpConsoleHandler();
365
        Log.get().setLevel(Level.FINE);
366
        final DesktopEnvironment de = getDE();
367
        System.out.println(de + " version " + de.getVersion());
17 ilm 368
    }
369
}