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 |
}
|