66c2d437262841ad6a81fcc2c3ecee199ce639d3
1 /* ownCloud Android client application
2 * Copyright (C) 2015 ownCloud Inc.
4 * This program is free software: you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License version 2,
6 * as published by the Free Software Foundation.
8 * This program is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 * GNU General Public License for more details.
13 * You should have received a copy of the GNU General Public License
14 * along with this program. If not, see <http://www.gnu.org/licenses/>.
18 package com
.owncloud
.android
.files
.services
;
20 import android
.accounts
.Account
;
21 import android
.util
.Pair
;
23 import com
.owncloud
.android
.datamodel
.OCFile
;
24 import com
.owncloud
.android
.lib
.common
.utils
.Log_OC
;
25 import com
.owncloud
.android
.operations
.UploadFileOperation
;
28 import java
.util
.ArrayList
;
29 import java
.util
.HashSet
;
30 import java
.util
.Iterator
;
32 import java
.util
.concurrent
.ConcurrentHashMap
;
33 import java
.util
.concurrent
.ConcurrentMap
;
36 * Helper structure to keep the trees of folders containing any file downloading or synchronizing.
38 * A map provides the indexation based in hashing.
40 * A tree is created per account.
42 * @author David A. Velasco
44 public class IndexedForest
<V
> {
46 private ConcurrentMap
<String
, Node
<V
>> mMap
= new ConcurrentHashMap
<String
, Node
<V
>>();
48 private class Node
<V
> {
50 Node
<V
> mParent
= null
;
51 Set
<Node
<V
>> mChildren
= new HashSet
<Node
<V
>>(); // TODO be careful with hash()
54 // payload is optional
55 public Node(String key
, V payload
) {
57 throw new IllegalArgumentException("Argument key MUST NOT be null");
63 public Node
<V
> getParent() {
67 public Set
<Node
<V
>> getChildren() {
71 public String
getKey() {
75 public V
getPayload() {
79 public void addChild(Node
<V
> child
) {
81 child
.setParent(this);
84 private void setParent(Node
<V
> parent
) {
88 public boolean hasChildren() {
89 return mChildren
.size() > 0;
92 public void removeChild(Node
<V
> removed
) {
93 mChildren
.remove(removed
);
96 public void clearPayload() {
102 public /* synchronized */ Pair
<String
, String
> putIfAbsent(Account account
, String remotePath
, V value
) {
103 String targetKey
= buildKey(account
, remotePath
);
104 Node
<V
> valuedNode
= new Node(targetKey
, value
);
110 String currentPath
= remotePath
, parentPath
= null
, parentKey
= null
;
111 Node
<V
> currentNode
= valuedNode
, parentNode
= null
;
112 boolean linked
= false
;
113 while (!OCFile
.ROOT_PATH
.equals(currentPath
) && !linked
) {
114 parentPath
= new File(currentPath
).getParent();
115 if (!parentPath
.endsWith(OCFile
.PATH_SEPARATOR
)) {
116 parentPath
+= OCFile
.PATH_SEPARATOR
;
118 parentKey
= buildKey(account
, parentPath
);
119 parentNode
= mMap
.get(parentKey
);
120 if (parentNode
== null
) {
121 parentNode
= new Node(parentKey
, null
);
122 parentNode
.addChild(currentNode
);
123 mMap
.put(parentKey
, parentNode
);
125 parentNode
.addChild(currentNode
);
128 currentPath
= parentPath
;
129 currentNode
= parentNode
;
132 String linkedTo
= OCFile
.ROOT_PATH
;
134 linkedTo
= parentNode
.getKey().substring(account
.name
.length());
136 return new Pair
<String
, String
>(targetKey
, linkedTo
);
140 public Pair
<V
, String
> removePayload(Account account
, String remotePath
) {
141 String targetKey
= buildKey(account
, remotePath
);
142 Node
<V
> target
= mMap
.get(targetKey
);
143 if (target
!= null
) {
144 target
.clearPayload();
145 if (!target
.hasChildren()) {
146 return remove(account
, remotePath
);
149 return new Pair
<V
, String
>(null
, null
);
153 public /* synchronized */ Pair
<V
, String
> remove(Account account
, String remotePath
) {
154 String targetKey
= buildKey(account
, remotePath
);
155 Node
<V
> firstRemoved
= mMap
.remove(targetKey
);
156 String unlinkedFrom
= null
;
158 if (firstRemoved
!= null
) {
160 removeDescendants(firstRemoved
);
162 /// remove ancestors if only here due to firstRemoved
163 Node
<V
> removed
= firstRemoved
;
164 Node
<V
> parent
= removed
.getParent();
165 boolean unlinked
= false
;
166 while (parent
!= null
) {
167 parent
.removeChild(removed
);
168 if (!parent
.hasChildren()) {
169 removed
= mMap
.remove(parent
.getKey());
170 parent
= removed
.getParent();
176 if (parent
!= null
) {
177 unlinkedFrom
= parent
.getKey().substring(account
.name
.length());
180 return new Pair
<V
, String
>(firstRemoved
.getPayload(), unlinkedFrom
);
183 return new Pair
<V
, String
>(null
, null
);
186 private void removeDescendants(Node
<V
> removed
) {
187 Iterator
<Node
<V
>> childrenIt
= removed
.getChildren().iterator();
188 Node
<V
> child
= null
;
189 while (childrenIt
.hasNext()) {
190 child
= childrenIt
.next();
191 mMap
.remove(child
.getKey());
192 removeDescendants(child
);
196 public boolean contains(Account account
, String remotePath
) {
197 String targetKey
= buildKey(account
, remotePath
);
198 return mMap
.containsKey(targetKey
);
201 public /* synchronized */ V
get(String key
) {
202 Node
<V
> node
= mMap
.get(key
);
204 return node
.getPayload();
210 public V
get(Account account
, String remotePath
) {
211 String key
= buildKey(account
, remotePath
);
216 public ConcurrentMap
<String
, Node
<V
>> get(Account account
){
217 ConcurrentMap
<String
, Node
<V
>> accountMap
= new ConcurrentHashMap
<String
, Node
<V
>>();
218 Iterator
<String
> it
= mMap
.keySet().iterator();
219 while (it
.hasNext()) {
220 String key
= it
.next();
221 Log_OC
.d("IndexedForest", "Number of pending downloads= " + mMap
.size());
222 if (key
.startsWith(account
.name
)) {
223 accountMap
.putIfAbsent(key
, mMap
.get(key
));
230 * Builds a key to index files
232 * @param account Account where the file to download is stored
233 * @param remotePath Path of the file in the server
235 private String
buildKey(Account account
, String remotePath
) {
236 return account
.name
+ remotePath
;