OpenConcerto

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

svn://code.openconcerto.org/openconcerto

Rev

Rev 174 | Rev 182 | Go to most recent revision | Show entire file | Regard whitespace | Details | Blame | Last modification | View Log | RSS feed

Rev 174 Rev 180
Line 12... Line 12...
12
 */
12
 */
13
 
13
 
14
 package org.openconcerto.utils;
14
 package org.openconcerto.utils;
15
 
15
 
16
import org.openconcerto.utils.CollectionMap2.Mode;
16
import org.openconcerto.utils.CollectionMap2.Mode;
-
 
17
import org.openconcerto.utils.DesktopEnvironment.Gnome;
17
import org.openconcerto.utils.OSFamily.Unix;
18
import org.openconcerto.utils.OSFamily.Unix;
18
import org.openconcerto.utils.ProcessStreams.Action;
-
 
19
import org.openconcerto.utils.StringUtils.Escaper;
19
import org.openconcerto.utils.StringUtils.Escaper;
20
import org.openconcerto.utils.cc.ExnTransformer;
20
import org.openconcerto.utils.cc.ExnTransformer;
21
import org.openconcerto.utils.cc.IClosure;
21
import org.openconcerto.utils.cc.IClosure;
22
 
22
 
23
import java.awt.Desktop;
23
import java.awt.Desktop;
Line 43... Line 43...
43
import java.nio.file.DirectoryStream;
43
import java.nio.file.DirectoryStream;
44
import java.nio.file.DirectoryStream.Filter;
44
import java.nio.file.DirectoryStream.Filter;
45
import java.nio.file.FileVisitResult;
45
import java.nio.file.FileVisitResult;
46
import java.nio.file.Files;
46
import java.nio.file.Files;
47
import java.nio.file.LinkOption;
47
import java.nio.file.LinkOption;
-
 
48
import java.nio.file.OpenOption;
48
import java.nio.file.Path;
49
import java.nio.file.Path;
49
import java.nio.file.SimpleFileVisitor;
50
import java.nio.file.SimpleFileVisitor;
50
import java.nio.file.StandardCopyOption;
51
import java.nio.file.StandardCopyOption;
-
 
52
import java.nio.file.StandardOpenOption;
51
import java.nio.file.attribute.BasicFileAttributes;
53
import java.nio.file.attribute.BasicFileAttributes;
52
import java.nio.file.attribute.PosixFileAttributeView;
54
import java.nio.file.attribute.PosixFileAttributeView;
53
import java.nio.file.attribute.PosixFilePermissions;
55
import java.nio.file.attribute.PosixFilePermissions;
54
import java.util.ArrayList;
56
import java.util.ArrayList;
55
import java.util.Arrays;
57
import java.util.Arrays;
Line 59... Line 61...
59
import java.util.HashMap;
61
import java.util.HashMap;
60
import java.util.HashSet;
62
import java.util.HashSet;
61
import java.util.List;
63
import java.util.List;
62
import java.util.Map;
64
import java.util.Map;
63
import java.util.Map.Entry;
65
import java.util.Map.Entry;
-
 
66
import java.util.Objects;
64
import java.util.Set;
67
import java.util.Set;
65
import java.util.logging.Level;
68
import java.util.logging.Level;
66
import java.util.regex.Pattern;
69
import java.util.regex.Pattern;
67
 
70
 
68
public final class FileUtils {
71
public final class FileUtils {
69
 
72
 
-
 
73
    public static void main(String[] args) throws Exception {
-
 
74
        final String cmd = args[0];
-
 
75
        if ("browseFile".equals(cmd))
-
 
76
            browseFile(new File(args[1]));
-
 
77
        else if ("browse".equals(cmd))
-
 
78
            browse(new URI(args[1]));
-
 
79
        else
-
 
80
            System.err.println("Unkown command : " + cmd);
-
 
81
    }
-
 
82
 
70
    private FileUtils() {
83
    private FileUtils() {
71
        // all static
84
        // all static
72
    }
85
    }
73
 
86
 
74
    public static void browseFile(final File f) throws IOException {
87
    public static void browseFile(final File f) throws IOException {
-
 
88
        browse(null, Objects.requireNonNull(f));
-
 
89
    }
75
 
90
 
-
 
91
    private static void browse(URI uri, final File f) throws IOException {
-
 
92
        assert (uri == null) != (f == null);
-
 
93
        boolean handled = false;
-
 
94
        final Desktop.Action action = Desktop.Action.BROWSE;
76
        if (Desktop.isDesktopSupported()) {
95
        if (isDesktopDesirable(action) && Desktop.isDesktopSupported()) {
77
            Desktop d = Desktop.getDesktop();
96
            final Desktop d = Desktop.getDesktop();
78
            if (d.isSupported(Desktop.Action.BROWSE)) {
97
            if (d.isSupported(action)) {
-
 
98
                if (uri == null)
79
                d.browse(f.getCanonicalFile().toURI());
99
                    uri = f.getCanonicalFile().toURI();
-
 
100
                if (!uri.getScheme().equals("file") && DesktopEnvironment.getDE() instanceof Gnome) {
80
            } else {
101
                    ProcessBuilder pb = null;
-
 
102
                    final String version = DesktopEnvironment.getDE().getVersion();
-
 
103
                    // Ubuntu 12.04, 14.04, 16.04
-
 
104
                    if (version.startsWith("3.4.") || version.startsWith("3.10.") || version.startsWith("3.18.")) {
-
 
105
                        pb = new ProcessBuilder("gvfs-mount", uri.toASCIIString());
81
                openNative(f);
106
                        // Ubuntu 18.04
-
 
107
                    } else if (version.startsWith("3.28")) {
-
 
108
                        // gio/gio-tool-mount.c#mount() calls g_file_mount_enclosing_volume.
-
 
109
                        // TODO find out how glib computes "the volume that contains the file
-
 
110
                        // location". E.g why does mount "davs://example.com/webdav/dir/dir" mounts
-
 
111
                        // "davs://example.com/webdav/".
-
 
112
                        // Return 0 if not yet mounted, 2 if it was already mounted.
-
 
113
                        pb = new ProcessBuilder("gio", "mount", uri.toASCIIString());
82
            }
114
                    }
-
 
115
                    if (pb != null) {
-
 
116
                        try {
-
 
117
                            startDiscardingOutput(pb).waitFor();
-
 
118
                        } catch (InterruptedException e) {
-
 
119
                            throw new RTInterruptedException("Interrupted while waiting on mount for " + uri, e);
-
 
120
                        }
-
 
121
                    }
-
 
122
                }
-
 
123
                d.browse(uri);
-
 
124
                handled = true;
83
        } else {
125
            }
-
 
126
        }
-
 
127
        if (!handled) {
-
 
128
            // if the caller passed a file use it instead of our converted URI
-
 
129
            if (f != null)
84
            openNative(f);
130
                openNative(f);
-
 
131
            else
-
 
132
                openNative(uri);
85
        }
133
        }
86
    }
134
    }
87
 
135
 
88
    public static void browse(URI uri) throws Exception {
136
    public static boolean isDesktopDesirable(Desktop.Action action) {
89
        final boolean windows = System.getProperty("os.name").startsWith("Windows");
137
        // apparently the JRE just checks if gnome libs are available (e.g. open Nautilus in XFCE)
90
        if (windows) {
-
 
91
            Desktop.getDesktop().browse(uri);
-
 
92
        } else {
-
 
93
            String[] cmdarray = new String[] { "xdg-open", uri.toString() };
-
 
94
            final int res = Runtime.getRuntime().exec(cmdarray).waitFor();
-
 
95
            if (res != 0)
-
 
96
                throw new IOException("error (" + res + ") executing " + Arrays.asList(cmdarray));
138
        return !(action == Desktop.Action.BROWSE && OSFamily.getInstance() == OSFamily.Linux && !(DesktopEnvironment.getDE() instanceof Gnome));
97
        }
139
    }
-
 
140
 
-
 
141
    public static void browse(URI uri) throws IOException {
-
 
142
        browse(Objects.requireNonNull(uri), null);
98
    }
143
    }
99
 
144
 
100
    public static void openFile(File f) throws IOException {
145
    public static void openFile(File f) throws IOException {
101
        if (!f.exists()) {
146
        if (!f.exists()) {
102
            throw new FileNotFoundException(f.getAbsolutePath() + " not found");
147
            throw new FileNotFoundException(f.getAbsolutePath() + " not found");
Line 673... Line 718...
673
    public static final byte[] readBytes(File f) throws IOException {
718
    public static final byte[] readBytes(File f) throws IOException {
674
        // works for /proc files which report 0 size
719
        // works for /proc files which report 0 size
675
        return Files.readAllBytes(f.toPath());
720
        return Files.readAllBytes(f.toPath());
676
    }
721
    }
677
 
722
 
-
 
723
    /**
-
 
724
     * Write the passed string with default charset to the passed file, truncating it.
-
 
725
     * 
-
 
726
     * @param s the string.
-
 
727
     * @param f the file.
-
 
728
     * @throws IOException if an error occurs.
-
 
729
     * @deprecated use {@link #writeUTF8(Path, String, OpenOption...)} or
-
 
730
     *             {@link #write2Step(String, Path)}
-
 
731
     */
678
    public static void write(String s, File f) throws IOException {
732
    public static void write(String s, File f) throws IOException {
679
        write(s, f, null, false);
733
        write2Step(s, f, Charset.defaultCharset(), false);
680
    }
734
    }
681
 
735
 
-
 
736
    public static void writeUTF8(String s, File f) throws IOException {
-
 
737
        writeUTF8(f.toPath(), s);
-
 
738
    }
-
 
739
 
-
 
740
    public static void writeUTF8(Path path, String s, OpenOption... options) throws IOException {
-
 
741
        Files.write(path, s.getBytes(StandardCharsets.UTF_8), options);
-
 
742
    }
-
 
743
 
682
    public static void write(String s, File f, Charset charset, boolean append) throws IOException {
744
    public static void write2Step(String s, File f, Charset charset, boolean append) throws IOException {
-
 
745
        write2Step(s, f.toPath(), charset, append);
-
 
746
    }
-
 
747
 
-
 
748
    public static void write2Step(final String s, final Path f) throws IOException {
-
 
749
        // UTF_8 default like Files.newBufferedReader()
-
 
750
        write2Step(s, f, StandardCharsets.UTF_8);
-
 
751
    }
-
 
752
 
-
 
753
    public static void write2Step(final String s, final Path f, final Charset charset) throws IOException {
-
 
754
        write2Step(s, f, charset, false);
-
 
755
    }
-
 
756
 
-
 
757
    public static void write2Step(final String s, final Path f, final Charset charset, final boolean append) throws IOException {
-
 
758
        // create temporary file in the same directory so we can move it
683
        try (final FileOutputStream fileStream = new FileOutputStream(f, append);
759
        final Path tmpFile = Files.createTempFile(f.toAbsolutePath().getParent(), null, null);
-
 
760
        try {
-
 
761
            // 1. write elsewhere
684
                final BufferedWriter w = new BufferedWriter(charset == null ? new OutputStreamWriter(fileStream) : new OutputStreamWriter(fileStream, charset))) {
762
            final byte[] bs = charset == null ? s.getBytes() : s.getBytes(charset);
-
 
763
            if (append) {
-
 
764
                // REPLACE_EXISTING since tmpFile exists
-
 
765
                Files.copy(f, tmpFile, StandardCopyOption.REPLACE_EXISTING);
-
 
766
                Files.write(tmpFile, bs, StandardOpenOption.APPEND);
-
 
767
            } else {
-
 
768
                Files.write(tmpFile, bs);
-
 
769
            }
-
 
770
            // 2. move into place (cannot use ATOMIC_MOVE because f might exists ; and we would need
-
 
771
            // to handle AtomicMoveNotSupportedException)
-
 
772
            Files.move(tmpFile, f, StandardCopyOption.REPLACE_EXISTING);
-
 
773
            // don't try to delete if move() successful
-
 
774
        } catch (RuntimeException | Error | IOException e) {
-
 
775
            try {
-
 
776
                Files.deleteIfExists(tmpFile);
-
 
777
            } catch (Exception e1) {
-
 
778
                e.addSuppressed(e1);
-
 
779
            }
685
            w.write(s);
780
            throw e;
686
        }
781
        }
687
    }
782
    }
688
 
783
 
689
    /**
784
    /**
690
     * Create a writer for the passed file, and write the XML declaration.
785
     * Create a writer for the passed file, and write the XML declaration.
Line 808... Line 903...
808
            // 1. needs CYGWIN=winsymlinks to create a shortcut, but even then "ln -f" doesn't work
903
            // 1. needs CYGWIN=winsymlinks to create a shortcut, but even then "ln -f" doesn't work
809
            // since it tries to delete l instead of l.LNK
904
            // since it tries to delete l instead of l.LNK
810
            // 2. it sets the system flag so "dir" doesn't show the shortcut (unless you add /AS)
905
            // 2. it sets the system flag so "dir" doesn't show the shortcut (unless you add /AS)
811
            // 3. the shortcut is recognized as a symlink thanks to a special attribute that can get
906
            // 3. the shortcut is recognized as a symlink thanks to a special attribute that can get
812
            // lost (e.g. copying in eclipse)
907
            // lost (e.g. copying in eclipse)
813
            ps = Runtime.getRuntime().exec(new String[] { "cscript", getShortCutFile().getAbsolutePath(), link.getAbsolutePath(), target.getCanonicalPath() });
908
            ps = startDiscardingOutput("cscript", getShortCutFile().getAbsolutePath(), link.getAbsolutePath(), target.getCanonicalPath());
814
            res = new File(link.getParentFile(), link.getName() + ".LNK");
909
            res = new File(link.getParentFile(), link.getName() + ".LNK");
815
        } else {
910
        } else {
816
            final String rel = FileUtils.relative(link.getAbsoluteFile().getParentFile(), target);
911
            final String rel = FileUtils.relative(link.getAbsoluteFile().getParentFile(), target);
817
            // add -f to replace existing links
912
            // add -f to replace existing links
818
            // add -n so that ln -sf aDir anExistantLinkToIt succeed
913
            // add -n so that ln -sf aDir anExistantLinkToIt succeed
819
            final String[] cmdarray = { "ln", "-sfn", rel, link.getAbsolutePath() };
914
            final String[] cmdarray = { "ln", "-sfn", rel, link.getAbsolutePath() };
820
            ps = Runtime.getRuntime().exec(cmdarray);
915
            ps = startDiscardingOutput(cmdarray);
821
            res = link;
916
            res = link;
822
        }
917
        }
823
        // no need for output, either it succeeds or it fails
-
 
824
        ProcessStreams.handle(ps, Action.CLOSE);
-
 
825
        try {
918
        try {
826
            final int exitValue = ps.waitFor();
919
            final int exitValue = ps.waitFor();
827
            if (exitValue == 0)
920
            if (exitValue == 0)
828
                return res;
921
                return res;
829
            else
922
            else
Line 883... Line 976...
883
     * and that it will not be called thousands of times per second.
976
     * and that it will not be called thousands of times per second.
884
     * 
977
     * 
885
     * @param prefix the prefix string to be used in generating the directory's name.
978
     * @param prefix the prefix string to be used in generating the directory's name.
886
     * @return the newly-created directory.
979
     * @return the newly-created directory.
887
     * @throws IllegalStateException if the directory could not be created.
980
     * @throws IllegalStateException if the directory could not be created.
-
 
981
     * @deprecated use
-
 
982
     *             {@link Files#createTempDirectory(String, java.nio.file.attribute.FileAttribute...)}
888
     */
983
     */
889
    public static File createTempDir(final String prefix) {
984
    public static File createTempDir(final String prefix) {
890
        final File baseDir = new File(System.getProperty("java.io.tmpdir"));
985
        final File baseDir = new File(System.getProperty("java.io.tmpdir"));
891
        final String baseName = prefix + System.currentTimeMillis() + "-";
986
        final String baseName = prefix + System.currentTimeMillis() + "-";
892
 
987
 
Line 916... Line 1011...
916
            openNative(f);
1011
            openNative(f);
917
        } catch (IOException exn) {
1012
        } catch (IOException exn) {
918
            for (int i = 0; i < executables.length; i++) {
1013
            for (int i = 0; i < executables.length; i++) {
919
                final String executable = executables[i];
1014
                final String executable = executables[i];
920
                try {
1015
                try {
921
                    ProcessStreams.handle(Runtime.getRuntime().exec(new String[] { executable, f.getCanonicalPath() }), Action.CLOSE);
1016
                    startDiscardingOutput(executable, f.getCanonicalPath());
922
                    return;
1017
                    return;
923
                } catch (IOException e) {
1018
                } catch (IOException e) {
-
 
1019
                    exn.addSuppressed(new IOException("unable to open with " + executable, e));
924
                    // try the next one
1020
                    // try the next one
925
                }
1021
                }
926
            }
1022
            }
927
            throw ExceptionUtils.createExn(IOException.class, "unable to open " + f + " with: " + Arrays.asList(executables), exn);
1023
            throw new IOException("unable to open " + f, exn);
928
        }
1024
        }
929
    }
1025
    }
930
 
1026
 
931
    /**
1027
    /**
932
     * Open the passed file as if it were graphically opened by the current user (user's "open
1028
     * Open the passed file as if it were graphically opened by the current user (user's "open
Line 934... Line 1030...
934
     * 
1030
     * 
935
     * @param f the file to open.
1031
     * @param f the file to open.
936
     * @throws IOException if f couldn't be opened.
1032
     * @throws IOException if f couldn't be opened.
937
     */
1033
     */
938
    private static final void openNative(File f) throws IOException {
1034
    private static final void openNative(File f) throws IOException {
-
 
1035
        openNative(f.getCanonicalPath());
-
 
1036
    }
-
 
1037
 
-
 
1038
    private static final void openNative(URI uri) throws IOException {
-
 
1039
        openNative(uri.toASCIIString());
-
 
1040
    }
-
 
1041
 
-
 
1042
    private static final void openNative(String param) throws IOException {
939
        final OSFamily os = OSFamily.getInstance();
1043
        final OSFamily os = OSFamily.getInstance();
940
        final String[] cmdarray;
1044
        final String[] cmdarray;
941
        if (os == OSFamily.Windows) {
1045
        if (os == OSFamily.Windows) {
942
            cmdarray = new String[] { "cmd", "/c", "start", "\"\"", f.getCanonicalPath() };
1046
            cmdarray = new String[] { "cmd", "/c", "start", "\"\"", param };
943
        } else if (os == OSFamily.Mac) {
1047
        } else if (os == OSFamily.Mac) {
944
            cmdarray = new String[] { "open", f.getCanonicalPath() };
1048
            cmdarray = new String[] { "open", param };
945
        } else if (os instanceof Unix) {
1049
        } else if (os instanceof Unix) {
946
            cmdarray = new String[] { "xdg-open", f.getCanonicalPath() };
1050
            cmdarray = new String[] { "xdg-open", param };
947
        } else {
1051
        } else {
948
            throw new IOException("unknown way to open " + f);
1052
            throw new IOException("unknown way to open " + param);
949
        }
1053
        }
950
        try {
1054
        try {
951
            final Process ps = Runtime.getRuntime().exec(cmdarray);
1055
            final Process ps = startDiscardingOutput(cmdarray);
952
            ProcessStreams.handle(ps, Action.CLOSE);
-
 
953
            // can wait since the command return as soon as the native application is launched
1056
            // can wait since the command return as soon as the native application is launched
954
            // (i.e. this won't wait 30s for OpenOffice)
1057
            // (i.e. this won't wait 30s for OpenOffice)
955
            final int res = ps.waitFor();
1058
            final int res = ps.waitFor();
956
            if (res != 0)
1059
            if (res != 0)
957
                throw new IOException("error (" + res + ") executing " + Arrays.asList(cmdarray));
1060
                throw new IOException("error (" + res + ") executing " + Arrays.asList(cmdarray));
958
        } catch (InterruptedException e) {
1061
        } catch (InterruptedException e) {
959
            throw ExceptionUtils.createExn(IOException.class, "interrupted waiting for " + Arrays.asList(cmdarray), e);
1062
            throw ExceptionUtils.createExn(IOException.class, "interrupted waiting for " + Arrays.asList(cmdarray), e);
960
        }
1063
        }
961
    }
1064
    }
962
 
1065
 
-
 
1066
    private static final Process startDiscardingOutput(final String... command) throws IOException {
-
 
1067
        return startDiscardingOutput(new ProcessBuilder(command));
-
 
1068
    }
-
 
1069
 
-
 
1070
    private static final Process startDiscardingOutput(final ProcessBuilder pb) throws IOException {
-
 
1071
        final Process res = pb.redirectOutput(ProcessStreams.DISCARD).redirectError(ProcessStreams.DISCARD).start();
-
 
1072
        res.getOutputStream().close();
-
 
1073
        return res;
-
 
1074
    }
-
 
1075
 
963
    static final boolean gnomeRunning() {
1076
    static final boolean gnomeRunning() {
964
        try {
1077
        try {
965
            final Process ps = Runtime.getRuntime().exec(new String[] { "pgrep", "-u", System.getProperty("user.name"), "nautilus" });
1078
            final Process ps = startDiscardingOutput("pgrep", "-u", System.getProperty("user.name"), "nautilus");
966
            // no need for output, use exit status
1079
            // no need for output, use exit status
967
            ProcessStreams.handle(ps, Action.CLOSE);
-
 
968
            return ps.waitFor() == 0;
1080
            return ps.waitFor() == 0;
969
        } catch (Exception e) {
1081
        } catch (Exception e) {
970
            return false;
1082
            return false;
971
        }
1083
        }
972
    }
1084
    }