OpenConcerto

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

svn://code.openconcerto.org/openconcerto

Rev

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

Rev Author Line No. Line
13 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.jopendocument.link;
15
 
16
import org.openconcerto.utils.DesktopEnvironment;
61 ilm 17
import org.openconcerto.utils.DesktopEnvironment.Mac;
13 ilm 18
 
19
import java.io.File;
20
import java.io.FileFilter;
21
import java.io.IOException;
22
import java.net.MalformedURLException;
23
import java.net.URL;
24
import java.util.ArrayList;
83 ilm 25
import java.util.Arrays;
13 ilm 26
import java.util.Collections;
27
import java.util.HashMap;
28
import java.util.List;
29
import java.util.Map;
30
import java.util.Set;
31
import java.util.regex.Matcher;
32
import java.util.regex.Pattern;
33
 
34
/**
35
 * This class finds out where OpenOffice.org is installed.
36
 *
37
 * @author Sylvain CUAZ
38
 * @see #getInstance()
39
 */
40
public class OOInstallation {
41
 
83 ilm 42
    public static final String PREFER_OPENOFFICE = "preferOpenOffice";
43
 
13 ilm 44
    private static OOInstallation instance;
45
 
46
    /**
47
     * Return the installation for this machine.
48
     *
49
     * @return the installation for this machine, <code>null</code> if not installed.
50
     * @throws IOException if an error occurs while searching.
51
     */
52
    public static OOInstallation getInstance() throws IOException {
53
        // since null means never detected or not installed, this will keep searching when not
54
        // installed
55
        if (instance == null) {
56
            instance = detectInstall();
57
        }
58
        return instance;
59
    }
60
 
61
    /**
62
     * Forget the current installation to pick up a change (e.g. updated version).
63
     */
64
    public static void reset() {
65
        instance = null;
66
    }
67
 
68
    // UREINSTALLLOCATION REG_SZ C:\Program Files\OpenOffice.org 3\URE\
69
    // \1 is the name, \2 the value
70
    // cannot use \p{} since some names/values can be non-ASCII
71
    private static final Pattern stringValuePattern = Pattern.compile("^\\s*(.+?)\\s+REG_SZ\\s+(.+?)$", Pattern.MULTILINE);
72
 
19 ilm 73
    private static final String LOBundleID = "org.libreoffice.script";
13 ilm 74
    private static final String OOBundleID = "org.openoffice.script";
75
 
76
    // return the standard out (not the standard error)
77
    private static String cmdSubstitution(String... args) throws IOException {
78
        final ProcessBuilder pb = new ProcessBuilder(args);
79
        pb.redirectErrorStream(false);
80
        return DesktopEnvironment.cmdSubstitution(pb.start());
81
    }
82
 
83
    static final URL toURL(final File f) {
84
        try {
85
            return f.toURI().toURL();
86
        } catch (MalformedURLException e) {
87
            // shouldn't happen since constructed from a file
88
            throw new IllegalStateException("Couldn't transform to URL " + f, e);
89
        }
90
    }
91
 
92
    // handle windows x64
93
    private static String findRootPath() {
83 ilm 94
        final String[] loRootPaths = { "HKEY_LOCAL_MACHINE\\SOFTWARE\\LibreOffice", "HKEY_LOCAL_MACHINE\\SOFTWARE\\Wow6432Node\\LibreOffice" };
95
        final String[] ooRootPaths = { "HKEY_LOCAL_MACHINE\\SOFTWARE\\OpenOffice", "HKEY_LOCAL_MACHINE\\SOFTWARE\\Wow6432Node\\OpenOffice", "HKEY_LOCAL_MACHINE\\SOFTWARE\\OpenOffice.org",
19 ilm 96
                "HKEY_LOCAL_MACHINE\\SOFTWARE\\Wow6432Node\\OpenOffice.org" };
83 ilm 97
        final List<String> rootPaths = new ArrayList<String>(loRootPaths.length + ooRootPaths.length);
98
        if (Boolean.getBoolean(PREFER_OPENOFFICE)) {
99
            rootPaths.addAll(Arrays.asList(ooRootPaths));
100
            rootPaths.addAll(Arrays.asList(loRootPaths));
101
        } else {
102
            rootPaths.addAll(Arrays.asList(loRootPaths));
103
            rootPaths.addAll(Arrays.asList(ooRootPaths));
104
        }
174 ilm 105
 
13 ilm 106
        for (final String p : rootPaths) {
174 ilm 107
            // On force à chercher dans le registre 64 bits sinon il va chercher dans le registre 32
108
            // bits si os 64b et VM 32b
109
            if (DesktopEnvironment.test("reg", "query", p, "/reg:64"))
13 ilm 110
                return p;
111
        }
174 ilm 112
 
13 ilm 113
        return null;
114
    }
115
 
61 ilm 116
    private static File findBundleDir() throws IOException {
117
        final Mac de = (Mac) DesktopEnvironment.getDE();
83 ilm 118
        final String[] bundleIDs = Boolean.getBoolean(PREFER_OPENOFFICE) ? new String[] { OOBundleID, LOBundleID } : new String[] { LOBundleID, OOBundleID };
119
        for (final String bundleID : bundleIDs) {
61 ilm 120
            final File url = de.getAppDir(bundleID);
121
            if (url != null)
19 ilm 122
                return url;
123
        }
124
        return null;
125
    }
126
 
13 ilm 127
    // all string values for the passed registry path
128
    private static Map<String, String> getStringValues(final String path, final String option) throws IOException {
129
        final Map<String, String> values = new HashMap<String, String>();
174 ilm 130
        // On force /reg:64 (utile si on utilise une VM 32 avec un systeme 64 bits)
131
        final String out = DesktopEnvironment.cmdSubstitution(Runtime.getRuntime().exec(new String[] { "reg", "query", path, option, "/reg:64" }));
13 ilm 132
        final Matcher matcher = stringValuePattern.matcher(out);
133
        while (matcher.find()) {
134
            values.put(matcher.group(1), matcher.group(2));
135
        }
136
        return values;
137
    }
138
 
139
    // add jar directories for OpenOffice 2 or 3
140
    private static final void addPaths(final List<File> cp, final File progDir, final String basisDir, final String ureDir) throws IOException {
141
        // oo2
142
        // all in C:\Program Files\OpenOffice.org 2.4\program\classes
143
        add(cp, new File(progDir, "classes"));
144
 
145
        // oo3
146
        // some in C:\Program Files\OpenOffice.org 3\URE\java
147
        // the rest in C:\Program Files\OpenOffice.org 3\Basis\program\classes
148
        if (ureDir != null) {
149
            // Windows
150
            add(cp, ureDir + File.separator + "java");
151
            // MacOS and Linux
152
            add(cp, ureDir + File.separator + "share" + File.separator + "java");
153
        }
144 ilm 154
        if (basisDir != null) {
13 ilm 155
            add(cp, basisDir + File.separator + "program" + File.separator + "classes");
144 ilm 156
            // at least for LO 5.3 on MacOS
157
            add(cp, basisDir + File.separator + "Resources" + File.separator + "java");
158
        }
13 ilm 159
    }
160
 
161
    private static final void addUnixPaths(final List<File> cp, final File progDir) throws IOException {
162
        final File baseDir = progDir.getParentFile();
61 ilm 163
        final File basisDir = new File(baseDir, "basis-link");
164
        // basis-link was dropped from LO 3.5
165
        final String basisPath = (basisDir.exists() ? basisDir : baseDir).getPath();
166
        final String ureDir = basisPath + File.separator + "ure-link";
167
        addPaths(cp, progDir, basisPath, ureDir);
13 ilm 168
    }
169
 
170
    private static void add(final List<File> res, final File f) {
61 ilm 171
        // e.g. on LO 3.5 BASIS is no longer 'Basis/' but './'
172
        if (f != null && f.isDirectory() && !res.contains(f)) {
13 ilm 173
            res.add(f);
174
        }
175
    }
176
 
177
    private static void add(final List<File> res, final String f) {
178
        if (f != null)
179
            add(res, new File(f));
180
    }
181
 
182
    private static OOInstallation detectInstall() throws IOException {
183
        final File exe;
184
        final List<File> cp = new ArrayList<File>(3);
185
 
186
        final String os = System.getProperty("os.name");
187
        if (os.startsWith("Windows")) {
188
            final String rootPath = findRootPath();
189
            // not installed
190
            if (rootPath == null)
191
                return null;
19 ilm 192
            final boolean libreOffice = rootPath.contains("LibreOffice");
13 ilm 193
 
194
            // Only the default value so pass '/ve'
195
            final Map<String, String> unoValues = getStringValues(rootPath + "\\UNO\\InstallPath", "/ve");
196
            if (unoValues.size() != 1)
197
                throw new IOException("No UNO install path: " + unoValues);
198
            // e.g. C:\Program Files\OpenOffice.org 2.4\program
199
            final File unoPath = new File(unoValues.values().iterator().next());
200
            if (!unoPath.isDirectory())
201
                throw new IOException(unoPath + " is not a directory");
202
            exe = new File(unoPath, "soffice.exe");
203
 
61 ilm 204
            // Perhaps check out parallel install but in Windows it's really cumbersome :
205
            // http://wiki.documentfoundation.org/Installing_in_parallel
206
 
207
            final String layerPath;
208
            if (!libreOffice) {
83 ilm 209
                layerPath = rootPath.contains("OpenOffice.org") ? "\\Layers\\OpenOffice.org" : "\\Layers\\OpenOffice";
61 ilm 210
            } else if (DesktopEnvironment.test("reg", "query", rootPath + "\\Layers")) {
211
                layerPath = "\\Layers\\LibreOffice";
212
            } else {
213
                // LO 3.4
214
                layerPath = "\\Layers_\\LibreOffice";
215
            }
13 ilm 216
            // '/s' since variables are one level (the version) deeper
61 ilm 217
            final Map<String, String> layersValues = getStringValues(rootPath + layerPath, "/s");
13 ilm 218
            addPaths(cp, unoPath, layersValues.get("BASISINSTALLLOCATION"), layersValues.get("UREINSTALLLOCATION"));
219
        } else if (os.startsWith("Mac OS")) {
61 ilm 220
            final File appPkg = findBundleDir();
221
            if (appPkg == null)
13 ilm 222
                return null;
61 ilm 223
            // need to call soffice from the MacOS directory otherwise it fails
224
            exe = new File(appPkg, "Contents/MacOS/soffice");
225
            addUnixPaths(cp, new File(appPkg, "Contents/program"));
13 ilm 226
        } else if (os.startsWith("Linux")) {
227
            // soffice is usually a symlink in /usr/bin
228
            // if not found prints nothing at all
229
            final String binPath = cmdSubstitution("which", "soffice").trim();
230
            if (binPath.length() != 0) {
231
                exe = new File(binPath).getCanonicalFile();
232
            } else {
233
                // e.g. Ubuntu 6.06
234
                final File defaultInstall = new File("/usr/lib/openoffice/program/soffice");
235
                exe = defaultInstall.canExecute() ? defaultInstall : null;
236
            }
237
            if (exe != null)
238
                addUnixPaths(cp, exe.getParentFile());
239
        } else
240
            exe = null;
241
        return exe == null ? null : new OOInstallation(exe, cp);
242
    }
243
 
244
    private final File executable;
245
    private final List<File> classpath;
246
 
247
    // TODO parse program/version.ini
248
 
249
    private OOInstallation(File executable, List<File> classpath) throws IOException {
250
        super();
251
        if (!executable.isFile())
252
            throw new IOException("executable not found at " + executable);
253
 
254
        this.executable = executable;
255
        this.classpath = Collections.unmodifiableList(new ArrayList<File>(classpath));
256
    }
257
 
258
    public final File getExecutable() {
259
        return this.executable;
260
    }
261
 
262
    public final List<File> getClasspath() {
263
        return this.classpath;
264
    }
265
 
266
    public final List<URL> getURLs(final Set<String> jars) {
267
        final int stop = this.getClasspath().size();
268
        final List<URL> res = new ArrayList<URL>();
269
        for (int i = 0; i < stop; i++) {
270
            final File[] foundJars = this.getClasspath().get(i).listFiles(new FileFilter() {
271
                @Override
272
                public boolean accept(File f) {
273
                    return jars.contains(f.getName());
274
                }
275
            });
276
            for (final File foundJar : foundJars) {
277
                res.add(toURL(foundJar));
278
            }
279
        }
280
        return res;
281
    }
282
 
283
    @Override
284
    public String toString() {
285
        return this.getClass().getSimpleName() + " exe: " + this.getExecutable() + " classpath: " + this.getClasspath();
286
    }
287
 
288
    public static void main(String[] args) {
289
        try {
290
            final OOInstallation i = getInstance();
291
            System.out.println(i == null ? "Not installed" : i);
292
        } catch (IOException e) {
293
            System.out.println("Couldn't detect OpenOffice.org: " + e.getLocalizedMessage());
294
            e.printStackTrace();
295
        }
296
    }
144 ilm 297
 
298
    /**
299
     * TODO add methods to call the program, but there's currently a bug preventing from running a
300
     * headless instance when there's already a running instance :
301
     * https://bugs.documentfoundation.org/show_bug.cgi?id=37531#c45
302
     * https://ask.libreoffice.org/en/question/1686/how-to-not-connect-to-a-running-instance/?answer=1701#post-id-1701
303
     *
304
     * <pre>
305
     tmpdir=`mktemp -d /tmp/libreoffice-XXXXXXXXXXXX`
306
     trap "rm -rf $tmpdir" EXIT INT
307
     libreoffice "-env:UserInstallation=file://$tmpdir" --headless --nolockcheck --convert-to pdf "$out"
308
     * </pre>
309
     */
13 ilm 310
}