OpenConcerto

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

svn://code.openconcerto.org/openconcerto

Rev

Rev 83 | Rev 174 | Go to most recent revision | 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
        }
13 ilm 105
        for (final String p : rootPaths) {
106
            if (DesktopEnvironment.test("reg", "query", p))
107
                return p;
108
        }
109
        return null;
110
    }
111
 
61 ilm 112
    private static File findBundleDir() throws IOException {
113
        final Mac de = (Mac) DesktopEnvironment.getDE();
83 ilm 114
        final String[] bundleIDs = Boolean.getBoolean(PREFER_OPENOFFICE) ? new String[] { OOBundleID, LOBundleID } : new String[] { LOBundleID, OOBundleID };
115
        for (final String bundleID : bundleIDs) {
61 ilm 116
            final File url = de.getAppDir(bundleID);
117
            if (url != null)
19 ilm 118
                return url;
119
        }
120
        return null;
121
    }
122
 
13 ilm 123
    // all string values for the passed registry path
124
    private static Map<String, String> getStringValues(final String path, final String option) throws IOException {
125
        final Map<String, String> values = new HashMap<String, String>();
126
        final String out = DesktopEnvironment.cmdSubstitution(Runtime.getRuntime().exec(new String[] { "reg", "query", path, option }));
127
        final Matcher matcher = stringValuePattern.matcher(out);
128
        while (matcher.find()) {
129
            values.put(matcher.group(1), matcher.group(2));
130
        }
131
        return values;
132
    }
133
 
134
    // add jar directories for OpenOffice 2 or 3
135
    private static final void addPaths(final List<File> cp, final File progDir, final String basisDir, final String ureDir) throws IOException {
136
        // oo2
137
        // all in C:\Program Files\OpenOffice.org 2.4\program\classes
138
        add(cp, new File(progDir, "classes"));
139
 
140
        // oo3
141
        // some in C:\Program Files\OpenOffice.org 3\URE\java
142
        // the rest in C:\Program Files\OpenOffice.org 3\Basis\program\classes
143
        if (ureDir != null) {
144
            // Windows
145
            add(cp, ureDir + File.separator + "java");
146
            // MacOS and Linux
147
            add(cp, ureDir + File.separator + "share" + File.separator + "java");
148
        }
144 ilm 149
        if (basisDir != null) {
13 ilm 150
            add(cp, basisDir + File.separator + "program" + File.separator + "classes");
144 ilm 151
            // at least for LO 5.3 on MacOS
152
            add(cp, basisDir + File.separator + "Resources" + File.separator + "java");
153
        }
13 ilm 154
    }
155
 
156
    private static final void addUnixPaths(final List<File> cp, final File progDir) throws IOException {
157
        final File baseDir = progDir.getParentFile();
61 ilm 158
        final File basisDir = new File(baseDir, "basis-link");
159
        // basis-link was dropped from LO 3.5
160
        final String basisPath = (basisDir.exists() ? basisDir : baseDir).getPath();
161
        final String ureDir = basisPath + File.separator + "ure-link";
162
        addPaths(cp, progDir, basisPath, ureDir);
13 ilm 163
    }
164
 
165
    private static void add(final List<File> res, final File f) {
61 ilm 166
        // e.g. on LO 3.5 BASIS is no longer 'Basis/' but './'
167
        if (f != null && f.isDirectory() && !res.contains(f)) {
13 ilm 168
            res.add(f);
169
        }
170
    }
171
 
172
    private static void add(final List<File> res, final String f) {
173
        if (f != null)
174
            add(res, new File(f));
175
    }
176
 
177
    private static OOInstallation detectInstall() throws IOException {
178
        final File exe;
179
        final List<File> cp = new ArrayList<File>(3);
180
 
181
        final String os = System.getProperty("os.name");
182
        if (os.startsWith("Windows")) {
183
            final String rootPath = findRootPath();
184
            // not installed
185
            if (rootPath == null)
186
                return null;
19 ilm 187
            final boolean libreOffice = rootPath.contains("LibreOffice");
13 ilm 188
 
189
            // Only the default value so pass '/ve'
190
            final Map<String, String> unoValues = getStringValues(rootPath + "\\UNO\\InstallPath", "/ve");
191
            if (unoValues.size() != 1)
192
                throw new IOException("No UNO install path: " + unoValues);
193
            // e.g. C:\Program Files\OpenOffice.org 2.4\program
194
            final File unoPath = new File(unoValues.values().iterator().next());
195
            if (!unoPath.isDirectory())
196
                throw new IOException(unoPath + " is not a directory");
197
            exe = new File(unoPath, "soffice.exe");
198
 
61 ilm 199
            // Perhaps check out parallel install but in Windows it's really cumbersome :
200
            // http://wiki.documentfoundation.org/Installing_in_parallel
201
 
202
            final String layerPath;
203
            if (!libreOffice) {
83 ilm 204
                layerPath = rootPath.contains("OpenOffice.org") ? "\\Layers\\OpenOffice.org" : "\\Layers\\OpenOffice";
61 ilm 205
            } else if (DesktopEnvironment.test("reg", "query", rootPath + "\\Layers")) {
206
                layerPath = "\\Layers\\LibreOffice";
207
            } else {
208
                // LO 3.4
209
                layerPath = "\\Layers_\\LibreOffice";
210
            }
13 ilm 211
            // '/s' since variables are one level (the version) deeper
61 ilm 212
            final Map<String, String> layersValues = getStringValues(rootPath + layerPath, "/s");
13 ilm 213
            addPaths(cp, unoPath, layersValues.get("BASISINSTALLLOCATION"), layersValues.get("UREINSTALLLOCATION"));
214
        } else if (os.startsWith("Mac OS")) {
61 ilm 215
            final File appPkg = findBundleDir();
216
            if (appPkg == null)
13 ilm 217
                return null;
61 ilm 218
            // need to call soffice from the MacOS directory otherwise it fails
219
            exe = new File(appPkg, "Contents/MacOS/soffice");
220
            addUnixPaths(cp, new File(appPkg, "Contents/program"));
13 ilm 221
        } else if (os.startsWith("Linux")) {
222
            // soffice is usually a symlink in /usr/bin
223
            // if not found prints nothing at all
224
            final String binPath = cmdSubstitution("which", "soffice").trim();
225
            if (binPath.length() != 0) {
226
                exe = new File(binPath).getCanonicalFile();
227
            } else {
228
                // e.g. Ubuntu 6.06
229
                final File defaultInstall = new File("/usr/lib/openoffice/program/soffice");
230
                exe = defaultInstall.canExecute() ? defaultInstall : null;
231
            }
232
            if (exe != null)
233
                addUnixPaths(cp, exe.getParentFile());
234
        } else
235
            exe = null;
236
        return exe == null ? null : new OOInstallation(exe, cp);
237
    }
238
 
239
    private final File executable;
240
    private final List<File> classpath;
241
 
242
    // TODO parse program/version.ini
243
 
244
    private OOInstallation(File executable, List<File> classpath) throws IOException {
245
        super();
246
        if (!executable.isFile())
247
            throw new IOException("executable not found at " + executable);
248
 
249
        this.executable = executable;
250
        this.classpath = Collections.unmodifiableList(new ArrayList<File>(classpath));
251
    }
252
 
253
    public final File getExecutable() {
254
        return this.executable;
255
    }
256
 
257
    public final List<File> getClasspath() {
258
        return this.classpath;
259
    }
260
 
261
    public final List<URL> getURLs(final Set<String> jars) {
262
        final int stop = this.getClasspath().size();
263
        final List<URL> res = new ArrayList<URL>();
264
        for (int i = 0; i < stop; i++) {
265
            final File[] foundJars = this.getClasspath().get(i).listFiles(new FileFilter() {
266
                @Override
267
                public boolean accept(File f) {
268
                    return jars.contains(f.getName());
269
                }
270
            });
271
            for (final File foundJar : foundJars) {
272
                res.add(toURL(foundJar));
273
            }
274
        }
275
        return res;
276
    }
277
 
278
    @Override
279
    public String toString() {
280
        return this.getClass().getSimpleName() + " exe: " + this.getExecutable() + " classpath: " + this.getClasspath();
281
    }
282
 
283
    public static void main(String[] args) {
284
        try {
285
            final OOInstallation i = getInstance();
286
            System.out.println(i == null ? "Not installed" : i);
287
        } catch (IOException e) {
288
            System.out.println("Couldn't detect OpenOffice.org: " + e.getLocalizedMessage());
289
            e.printStackTrace();
290
        }
291
    }
144 ilm 292
 
293
    /**
294
     * TODO add methods to call the program, but there's currently a bug preventing from running a
295
     * headless instance when there's already a running instance :
296
     * https://bugs.documentfoundation.org/show_bug.cgi?id=37531#c45
297
     * https://ask.libreoffice.org/en/question/1686/how-to-not-connect-to-a-running-instance/?answer=1701#post-id-1701
298
     *
299
     * <pre>
300
     tmpdir=`mktemp -d /tmp/libreoffice-XXXXXXXXXXXX`
301
     trap "rm -rf $tmpdir" EXIT INT
302
     libreoffice "-env:UserInstallation=file://$tmpdir" --headless --nolockcheck --convert-to pdf "$out"
303
     * </pre>
304
     */
13 ilm 305
}