1 /*
2  * Copyright (C) 2010 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package com.android.gallery3d.data;
18 
19 import com.android.gallery3d.common.Utils;
20 import com.android.gallery3d.util.IdentityCache;
21 
22 import java.lang.ref.WeakReference;
23 import java.util.ArrayList;
24 
25 public class Path {
26     private static final String TAG = "Path";
27     private static Path sRoot = new Path(null, "ROOT");
28 
29     private final Path mParent;
30     private final String mSegment;
31     private WeakReference<MediaObject> mObject;
32     private IdentityCache<String, Path> mChildren;
33 
Path(Path parent, String segment)34     private Path(Path parent, String segment) {
35         mParent = parent;
36         mSegment = segment;
37     }
38 
getChild(String segment)39     public Path getChild(String segment) {
40         synchronized (Path.class) {
41             if (mChildren == null) {
42                 mChildren = new IdentityCache<String, Path>();
43             } else {
44                 Path p = mChildren.get(segment);
45                 if (p != null) return p;
46             }
47 
48             Path p = new Path(this, segment);
49             mChildren.put(segment, p);
50             return p;
51         }
52     }
53 
getParent()54     public Path getParent() {
55         synchronized (Path.class) {
56             return mParent;
57         }
58     }
59 
getChild(int segment)60     public Path getChild(int segment) {
61         return getChild(String.valueOf(segment));
62     }
63 
getChild(long segment)64     public Path getChild(long segment) {
65         return getChild(String.valueOf(segment));
66     }
67 
setObject(MediaObject object)68     public void setObject(MediaObject object) {
69         synchronized (Path.class) {
70             Utils.assertTrue(mObject == null || mObject.get() == null);
71             mObject = new WeakReference<MediaObject>(object);
72         }
73     }
74 
getObject()75     MediaObject getObject() {
76         synchronized (Path.class) {
77             return (mObject == null) ? null : mObject.get();
78         }
79     }
80 
81     @Override
82     // TODO: toString() should be more efficient, will fix it later
toString()83     public String toString() {
84         synchronized (Path.class) {
85             StringBuilder sb = new StringBuilder();
86             String[] segments = split();
87             for (int i = 0; i < segments.length; i++) {
88                 sb.append("/");
89                 sb.append(segments[i]);
90             }
91             return sb.toString();
92         }
93     }
94 
equalsIgnoreCase(String p)95     public boolean equalsIgnoreCase (String p) {
96         String path = toString();
97         return path.equalsIgnoreCase(p);
98     }
99 
fromString(String s)100     public static Path fromString(String s) {
101         synchronized (Path.class) {
102             String[] segments = split(s);
103             Path current = sRoot;
104             for (int i = 0; i < segments.length; i++) {
105                 current = current.getChild(segments[i]);
106             }
107             return current;
108         }
109     }
110 
split()111     public String[] split() {
112         synchronized (Path.class) {
113             int n = 0;
114             for (Path p = this; p != sRoot; p = p.mParent) {
115                 n++;
116             }
117             String[] segments = new String[n];
118             int i = n - 1;
119             for (Path p = this; p != sRoot; p = p.mParent) {
120                 segments[i--] = p.mSegment;
121             }
122             return segments;
123         }
124     }
125 
split(String s)126     public static String[] split(String s) {
127         int n = s.length();
128         if (n == 0) return new String[0];
129         if (s.charAt(0) != '/') {
130             throw new RuntimeException("malformed path:" + s);
131         }
132         ArrayList<String> segments = new ArrayList<String>();
133         int i = 1;
134         while (i < n) {
135             int brace = 0;
136             int j;
137             for (j = i; j < n; j++) {
138                 char c = s.charAt(j);
139                 if (c == '{') ++brace;
140                 else if (c == '}') --brace;
141                 else if (brace == 0 && c == '/') break;
142             }
143             if (brace != 0) {
144                 throw new RuntimeException("unbalanced brace in path:" + s);
145             }
146             segments.add(s.substring(i, j));
147             i = j + 1;
148         }
149         String[] result = new String[segments.size()];
150         segments.toArray(result);
151         return result;
152     }
153 
154     // Splits a string to an array of strings.
155     // For example, "{foo,bar,baz}" -> {"foo","bar","baz"}.
splitSequence(String s)156     public static String[] splitSequence(String s) {
157         int n = s.length();
158         if (s.charAt(0) != '{' || s.charAt(n-1) != '}') {
159             throw new RuntimeException("bad sequence: " + s);
160         }
161         ArrayList<String> segments = new ArrayList<String>();
162         int i = 1;
163         while (i < n - 1) {
164             int brace = 0;
165             int j;
166             for (j = i; j < n - 1; j++) {
167                 char c = s.charAt(j);
168                 if (c == '{') ++brace;
169                 else if (c == '}') --brace;
170                 else if (brace == 0 && c == ',') break;
171             }
172             if (brace != 0) {
173                 throw new RuntimeException("unbalanced brace in path:" + s);
174             }
175             segments.add(s.substring(i, j));
176             i = j + 1;
177         }
178         String[] result = new String[segments.size()];
179         segments.toArray(result);
180         return result;
181     }
182 
getPrefix()183     public String getPrefix() {
184         if (this == sRoot) return "";
185         return getPrefixPath().mSegment;
186     }
187 
getPrefixPath()188     public Path getPrefixPath() {
189         synchronized (Path.class) {
190             Path current = this;
191             if (current == sRoot) {
192                 throw new IllegalStateException();
193             }
194             while (current.mParent != sRoot) {
195                 current = current.mParent;
196             }
197             return current;
198         }
199     }
200 
getSuffix()201     public String getSuffix() {
202         // We don't need lock because mSegment is final.
203         return mSegment;
204     }
205 
206     // Below are for testing/debugging only
clearAll()207     static void clearAll() {
208         synchronized (Path.class) {
209             sRoot = new Path(null, "");
210         }
211     }
212 
dumpAll()213     static void dumpAll() {
214         dumpAll(sRoot, "", "");
215     }
216 
dumpAll(Path p, String prefix1, String prefix2)217     static void dumpAll(Path p, String prefix1, String prefix2) {
218         synchronized (Path.class) {
219             MediaObject obj = p.getObject();
220             Log.d(TAG, prefix1 + p.mSegment + ":"
221                     + (obj == null ? "null" : obj.getClass().getSimpleName()));
222             if (p.mChildren != null) {
223                 ArrayList<String> childrenKeys = p.mChildren.keys();
224                 int i = 0, n = childrenKeys.size();
225                 for (String key : childrenKeys) {
226                     Path child = p.mChildren.get(key);
227                     if (child == null) {
228                         ++i;
229                         continue;
230                     }
231                     Log.d(TAG, prefix2 + "|");
232                     if (++i < n) {
233                         dumpAll(child, prefix2 + "+-- ", prefix2 + "|   ");
234                     } else {
235                         dumpAll(child, prefix2 + "+-- ", prefix2 + "    ");
236                     }
237                 }
238             }
239         }
240     }
241 }
242