Skip to content

Commit 870b6bb

Browse files
marioAndyScherzinger
authored andcommitted
Move to approach without reflection
1 parent 39228a0 commit 870b6bb

File tree

2 files changed

+348
-14
lines changed

2 files changed

+348
-14
lines changed
Lines changed: 343 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,343 @@
1+
/*
2+
* Licensed to the Apache Software Foundation (ASF) under one or more
3+
* contributor license agreements. See the NOTICE file distributed with
4+
* this work for additional information regarding copyright ownership.
5+
* The ASF licenses this file to You under the Apache License, Version 2.0
6+
* (the "License"); you may not use this file except in compliance with
7+
* the License. You may obtain a copy of the License at
8+
*
9+
* http://www.apache.org/licenses/LICENSE-2.0
10+
*
11+
* Unless required by applicable law or agreed to in writing, software
12+
* distributed under the License is distributed on an "AS IS" BASIS,
13+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14+
* See the License for the specific language governing permissions and
15+
* limitations under the License.
16+
*
17+
* Modified a bit by Mario Danic
18+
* Changes are Copyright (C) 2017 Mario Danic
19+
*
20+
* Those changes are under the following licence:
21+
*
22+
* * This program is free software: you can redistribute it and/or modify
23+
* it under the terms of the GNU Affero General Public License as published by
24+
* the Free Software Foundation, either version 3 of the License, or
25+
* at your option) any later version.
26+
*
27+
* This program is distributed in the hope that it will be useful,
28+
* but WITHOUT ANY WARRANTY; without even the implied warranty of
29+
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
30+
* GNU Affero General Public License for more details.
31+
*
32+
* You should have received a copy of the GNU Affero General Public License
33+
* along with this program. If not, see <http://www.gnu.org/licenses/>.
34+
*/
35+
package com.owncloud.android.services.observer;
36+
37+
import org.apache.commons.io.FileUtils;
38+
import org.apache.commons.io.comparator.NameFileComparator;
39+
import org.apache.commons.io.monitor.FileAlterationListener;
40+
import org.apache.commons.io.monitor.FileAlterationObserver;
41+
import org.apache.commons.io.monitor.FileEntry;
42+
43+
import java.io.File;
44+
import java.io.FileFilter;
45+
import java.io.Serializable;
46+
import java.util.Arrays;
47+
import java.util.Comparator;
48+
import java.util.List;
49+
import java.util.concurrent.CopyOnWriteArrayList;
50+
51+
public class FileAlterationMagicObserver extends FileAlterationObserver implements Serializable {
52+
53+
private static final long serialVersionUID = 1185122225658782848L;
54+
private final List<FileAlterationListener> listeners = new CopyOnWriteArrayList<>();
55+
private FileEntry rootEntry;
56+
private FileFilter fileFilter;
57+
private Comparator<File> comparator;
58+
59+
static final FileEntry[] EMPTY_ENTRIES = new FileEntry[0];
60+
61+
62+
public FileAlterationMagicObserver(File directory, FileFilter fileFilter) {
63+
super(directory, fileFilter);
64+
65+
this.rootEntry = new FileEntry(directory);
66+
this.fileFilter = fileFilter;
67+
comparator = NameFileComparator.NAME_SYSTEM_COMPARATOR;
68+
}
69+
70+
/**
71+
* Return the directory being observed.
72+
*
73+
* @return the directory being observed
74+
*/
75+
public File getDirectory() {
76+
return rootEntry.getFile();
77+
}
78+
79+
/**
80+
* Return the fileFilter.
81+
*
82+
* @return the fileFilter
83+
* @since 2.1
84+
*/
85+
public FileFilter getFileFilter() {
86+
return fileFilter;
87+
}
88+
89+
public FileEntry getRootEntry() {
90+
return rootEntry;
91+
}
92+
93+
public void setRootEntry(FileEntry rootEntry) {
94+
this.rootEntry = rootEntry;
95+
}
96+
97+
/**
98+
* Add a file system listener.
99+
*
100+
* @param listener The file system listener
101+
*/
102+
public void addListener(final FileAlterationListener listener) {
103+
if (listener != null) {
104+
listeners.add(listener);
105+
}
106+
}
107+
108+
/**
109+
* Remove a file system listener.
110+
*
111+
* @param listener The file system listener
112+
*/
113+
public void removeListener(final FileAlterationListener listener) {
114+
if (listener != null) {
115+
while (listeners.remove(listener)) {
116+
}
117+
}
118+
}
119+
120+
/**
121+
* Returns the set of registered file system listeners.
122+
*
123+
* @return The file system listeners
124+
*/
125+
public Iterable<FileAlterationListener> getListeners() {
126+
return listeners;
127+
}
128+
129+
/**
130+
* Does nothing - hack for the monitor
131+
*
132+
*
133+
*/
134+
public void initialize() {
135+
}
136+
137+
138+
/**
139+
* Initializes everything
140+
*
141+
* @throws Exception if an error occurs
142+
*/
143+
public void init() throws Exception {
144+
rootEntry.refresh(rootEntry.getFile());
145+
final FileEntry[] children = doListFiles(rootEntry.getFile(), rootEntry);
146+
rootEntry.setChildren(children);
147+
}
148+
149+
150+
/**
151+
* Final processing.
152+
*
153+
* @throws Exception if an error occurs
154+
*/
155+
public void destroy() throws Exception {
156+
}
157+
158+
/**
159+
* Check whether the file and its chlidren have been created, modified or deleted.
160+
*/
161+
public void checkAndNotify() {
162+
163+
/* fire onStart() */
164+
for (final FileAlterationListener listener : listeners) {
165+
listener.onStart(this);
166+
}
167+
168+
/* fire directory/file events */
169+
final File rootFile = rootEntry.getFile();
170+
if (rootFile.exists()) {
171+
checkAndNotify(rootEntry, rootEntry.getChildren(), listFiles(rootFile));
172+
} else if (rootEntry.isExists()) {
173+
checkAndNotify(rootEntry, rootEntry.getChildren(), FileUtils.EMPTY_FILE_ARRAY);
174+
} else {
175+
// Didn't exist and still doesn't
176+
}
177+
178+
/* fire onStop() */
179+
for (final FileAlterationListener listener : listeners) {
180+
listener.onStop(this);
181+
}
182+
}
183+
184+
/**
185+
* Compare two file lists for files which have been created, modified or deleted.
186+
*
187+
* @param parent The parent entry
188+
* @param previous The original list of files
189+
* @param files The current list of files
190+
*/
191+
private void checkAndNotify(final FileEntry parent, final FileEntry[] previous, final File[] files) {
192+
int c = 0;
193+
final FileEntry[] current = files.length > 0 ? new FileEntry[files.length] : EMPTY_ENTRIES;
194+
for (final FileEntry entry : previous) {
195+
while (c < files.length && comparator.compare(entry.getFile(), files[c]) > 0) {
196+
current[c] = createFileEntry(parent, files[c]);
197+
doCreate(current[c]);
198+
c++;
199+
}
200+
if (c < files.length && comparator.compare(entry.getFile(), files[c]) == 0) {
201+
doMatch(entry, files[c]);
202+
checkAndNotify(entry, entry.getChildren(), listFiles(files[c]));
203+
current[c] = entry;
204+
c++;
205+
} else {
206+
checkAndNotify(entry, entry.getChildren(), FileUtils.EMPTY_FILE_ARRAY);
207+
doDelete(entry);
208+
}
209+
}
210+
for (; c < files.length; c++) {
211+
current[c] = createFileEntry(parent, files[c]);
212+
doCreate(current[c]);
213+
}
214+
parent.setChildren(current);
215+
}
216+
217+
/**
218+
* Create a new file entry for the specified file.
219+
*
220+
* @param parent The parent file entry
221+
* @param file The file to create an entry for
222+
* @return A new file entry
223+
*/
224+
private FileEntry createFileEntry(final FileEntry parent, final File file) {
225+
final FileEntry entry = parent.newChildInstance(file);
226+
entry.refresh(file);
227+
final FileEntry[] children = doListFiles(file, entry);
228+
entry.setChildren(children);
229+
return entry;
230+
}
231+
232+
/**
233+
* List the files
234+
*
235+
* @param file The file to list files for
236+
* @param entry the parent entry
237+
* @return The child files
238+
*/
239+
private FileEntry[] doListFiles(File file, FileEntry entry) {
240+
final File[] files = listFiles(file);
241+
final FileEntry[] children = files.length > 0 ? new FileEntry[files.length] : EMPTY_ENTRIES;
242+
for (int i = 0; i < files.length; i++) {
243+
children[i] = createFileEntry(entry, files[i]);
244+
}
245+
return children;
246+
}
247+
248+
/**
249+
* Fire directory/file created events to the registered listeners.
250+
*
251+
* @param entry The file entry
252+
*/
253+
private void doCreate(final FileEntry entry) {
254+
for (final FileAlterationListener listener : listeners) {
255+
if (entry.isDirectory()) {
256+
listener.onDirectoryCreate(entry.getFile());
257+
} else {
258+
listener.onFileCreate(entry.getFile());
259+
}
260+
}
261+
final FileEntry[] children = entry.getChildren();
262+
for (final FileEntry aChildren : children) {
263+
doCreate(aChildren);
264+
}
265+
}
266+
267+
/**
268+
* Fire directory/file change events to the registered listeners.
269+
*
270+
* @param entry The previous file system entry
271+
* @param file The current file
272+
*/
273+
private void doMatch(final FileEntry entry, final File file) {
274+
if (entry.refresh(file)) {
275+
for (final FileAlterationListener listener : listeners) {
276+
if (entry.isDirectory()) {
277+
listener.onDirectoryChange(file);
278+
} else {
279+
listener.onFileChange(file);
280+
}
281+
}
282+
}
283+
}
284+
285+
/**
286+
* Fire directory/file delete events to the registered listeners.
287+
*
288+
* @param entry The file entry
289+
*/
290+
private void doDelete(final FileEntry entry) {
291+
for (final FileAlterationListener listener : listeners) {
292+
if (entry.isDirectory()) {
293+
listener.onDirectoryDelete(entry.getFile());
294+
} else {
295+
listener.onFileDelete(entry.getFile());
296+
}
297+
}
298+
}
299+
300+
/**
301+
* List the contents of a directory
302+
*
303+
* @param file The file to list the contents of
304+
* @return the directory contents or a zero length array if
305+
* the empty or the file is not a directory
306+
*/
307+
private File[] listFiles(final File file) {
308+
File[] children = null;
309+
if (file.isDirectory()) {
310+
children = fileFilter == null ? file.listFiles() : file.listFiles(fileFilter);
311+
}
312+
if (children == null) {
313+
children = FileUtils.EMPTY_FILE_ARRAY;
314+
}
315+
if (comparator != null && children.length > 1) {
316+
Arrays.sort(children, comparator);
317+
}
318+
return children;
319+
}
320+
321+
/**
322+
* Provide a String representation of this observer.
323+
*
324+
* @return a String representation of this observer
325+
*/
326+
@Override
327+
public String toString() {
328+
final StringBuilder builder = new StringBuilder();
329+
builder.append(getClass().getSimpleName());
330+
builder.append("[file='");
331+
builder.append(getDirectory().getPath());
332+
builder.append('\'');
333+
if (fileFilter != null) {
334+
builder.append(", ");
335+
builder.append(fileFilter.toString());
336+
}
337+
builder.append(", listeners=");
338+
builder.append(listeners.size());
339+
builder.append("]");
340+
return builder.toString();
341+
}
342+
343+
}

src/com/owncloud/android/services/observer/SyncedFolderObserverService.java

Lines changed: 5 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,6 @@
4545
import java.io.FileOutputStream;
4646
import java.io.IOException;
4747
import java.io.ObjectOutputStream;
48-
import java.lang.reflect.Field;
4948
import java.util.ArrayList;
5049
import java.util.HashMap;
5150

@@ -73,7 +72,6 @@ public boolean accept(File pathname) {
7372
}
7473
};
7574

76-
FileEntry rootEntry;
7775

7876
FileOutputStream fos = null;
7977
ArrayList<Pair<SyncedFolder, FileEntry>> pairArrayList = new ArrayList<>();
@@ -82,19 +80,13 @@ public boolean accept(File pathname) {
8280
for (SyncedFolder syncedFolder : mProvider.getSyncedFolders()) {
8381
if (syncedFolder.isEnabled() && !syncedFolderMap.containsKey(syncedFolder.getLocalPath())) {
8482
Log_OC.d(TAG, "start observer: " + syncedFolder.getLocalPath());
85-
FileAlterationObserver observer = new FileAlterationObserver(syncedFolder.getLocalPath(), fileFilter);
86-
Field f;
83+
FileAlterationMagicObserver observer = new FileAlterationMagicObserver(new File(
84+
syncedFolder.getLocalPath()), fileFilter);
85+
8786
try {
88-
observer.initialize();
89-
f = observer.getClass().getDeclaredField("rootEntry");
90-
f.setAccessible(true);
91-
rootEntry = (FileEntry) f.get(observer);
92-
Pair<SyncedFolder, FileEntry> pair = new Pair<>(syncedFolder, rootEntry);
87+
observer.init();
88+
Pair<SyncedFolder, FileEntry> pair = new Pair<>(syncedFolder, observer.getRootEntry());
9389
pairArrayList.add(pair);
94-
} catch (NoSuchFieldException e) {
95-
Log_OC.d(TAG, "Failed getting private field rootEntry via NoSuchFieldException");
96-
} catch (IllegalAccessException e) {
97-
Log_OC.d(TAG, "Failed getting private field rootEntry via IllegalAccessException");
9890
} catch (Exception e) {
9991
Log_OC.d(TAG, "Failed getting an observer to intialize");
10092
}
@@ -120,7 +112,6 @@ public boolean accept(File pathname) {
120112
}
121113

122114

123-
124115
try {
125116
monitor.start();
126117
} catch (Exception e) {

0 commit comments

Comments
 (0)