daf46a568039dba8b5b4dabf80e5e2aae873f045
[pub/Android/ownCloud.git] / src / com / owncloud / android / files / services / IndexedForest.java
1 /* ownCloud Android client application
2 * Copyright (C) 2015 ownCloud Inc.
3 *
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.
7 *
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.
12 *
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/>.
15 *
16 */
17
18 package com.owncloud.android.files.services;
19
20 import android.accounts.Account;
21
22 import com.owncloud.android.datamodel.OCFile;
23
24 import java.io.File;
25 import java.util.HashSet;
26 import java.util.Set;
27 import java.util.concurrent.ConcurrentHashMap;
28 import java.util.concurrent.ConcurrentMap;
29
30 /**
31 * Helper structure to keep the trees of folders containing any file downloading or synchronizing.
32 *
33 * A map provides the indexation based in hashing.
34 *
35 * A tree is created per account.
36 *
37 * @author David A. Velasco
38 */
39 public class IndexedForest<V> {
40
41 private ConcurrentMap<String, Node<V>> mMap = new ConcurrentHashMap<String, Node<V>>();
42
43 private class Node<V> {
44 String mKey = null;
45 Node<V> mParent = null;
46 Set<Node<V>> mChildren = new HashSet<Node<V>>(); // TODO be careful with hash()
47 V mPayload = null;
48
49 // payload is optional
50 public Node(String key, V payload) {
51 if (key == null) {
52 throw new IllegalArgumentException("Argument key MUST NOT be null");
53 }
54 mKey = key;
55 mPayload = payload;
56 }
57
58 public Node<V> getParent() {
59 return mParent;
60 };
61
62 public Set<Node<V>> getChildren() {
63 return mChildren;
64 }
65
66 public String getKey() {
67 return mKey;
68 }
69
70 public V getPayload() {
71 return mPayload;
72 }
73
74 public void addChild(Node<V> child) {
75 mChildren.add(child);
76 child.setParent(this);
77 }
78
79 private void setParent(Node<V> parent) {
80 mParent = parent;
81 }
82
83 public boolean hasChildren() {
84 return mChildren.size() > 0;
85 }
86
87 public void removeChild(Node<V> removed) {
88 mChildren.remove(removed);
89 }
90 }
91
92
93 public /* synchronized */ String putIfAbsent(Account account, String remotePath, V value) {
94 String targetKey = buildKey(account, remotePath);
95 Node<V> valuedNode = new Node(targetKey, value);
96 mMap.putIfAbsent(
97 targetKey,
98 valuedNode
99 );
100
101 String currentPath = remotePath, parentPath = null, parentKey = null;
102 Node<V> currentNode = valuedNode, parentNode = null;
103 boolean linked = false;
104 while (!OCFile.ROOT_PATH.equals(currentPath) && !linked) {
105 parentPath = new File(currentPath).getParent();
106 if (!parentPath.endsWith(OCFile.PATH_SEPARATOR)) {
107 parentPath += OCFile.PATH_SEPARATOR;
108 }
109 parentKey = buildKey(account, parentPath);
110 parentNode = mMap.get(parentKey);
111 if (parentNode == null) {
112 parentNode = new Node(parentKey, null);
113 parentNode.addChild(currentNode);
114 mMap.put(parentKey, parentNode);
115 } else {
116 parentNode.addChild(currentNode);
117 linked = true;
118 }
119 currentPath = parentPath;
120 currentNode = parentNode;
121 }
122
123 return targetKey;
124 };
125
126 public /* synchronized */ V remove(Account account, String remotePath) {
127 String targetKey = buildKey(account, remotePath);
128 Node<V> firstRemoved = mMap.remove(targetKey);
129
130 if (firstRemoved != null) {
131 Node<V> removed = firstRemoved;
132 Node<V> parent = removed.getParent();
133 while (parent != null) {
134 parent.removeChild(removed);
135 if (!parent.hasChildren()) {
136 removed = mMap.remove(parent.getKey());
137 parent = removed.getParent();
138 } else {
139 parent = null;
140 }
141 }
142 }
143
144 if (firstRemoved != null) {
145 return firstRemoved.getPayload();
146 } else {
147 return null;
148 }
149
150 }
151
152 public boolean contains(Account account, String remotePath) {
153 String targetKey = buildKey(account, remotePath);
154 return mMap.containsKey(targetKey);
155 }
156
157 public /* synchronized */ V get(String key) {
158 Node<V> node = mMap.get(key);
159 if (node != null) {
160 return node.getPayload();
161 } else {
162 return null;
163 }
164 }
165
166
167 /**
168 * Builds a key to index files
169 *
170 * @param account Account where the file to download is stored
171 * @param remotePath Path of the file in the server
172 */
173 private String buildKey(Account account, String remotePath) {
174 return account.name + remotePath;
175 }
176
177
178
179 }