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 17package com.android.gallery3d.data; 18 19import com.android.gallery3d.common.Utils; 20import com.android.gallery3d.util.IdentityCache; 21 22import java.lang.ref.WeakReference; 23import java.util.ArrayList; 24 25public 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 34 private Path(Path parent, String segment) { 35 mParent = parent; 36 mSegment = segment; 37 } 38 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 54 public Path getParent() { 55 synchronized (Path.class) { 56 return mParent; 57 } 58 } 59 60 public Path getChild(int segment) { 61 return getChild(String.valueOf(segment)); 62 } 63 64 public Path getChild(long segment) { 65 return getChild(String.valueOf(segment)); 66 } 67 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 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 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 95 public boolean equalsIgnoreCase (String p) { 96 String path = toString(); 97 return path.equalsIgnoreCase(p); 98 } 99 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 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 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"}. 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 183 public String getPrefix() { 184 if (this == sRoot) return ""; 185 return getPrefixPath().mSegment; 186 } 187 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 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 207 static void clearAll() { 208 synchronized (Path.class) { 209 sRoot = new Path(null, ""); 210 } 211 } 212 213 static void dumpAll() { 214 dumpAll(sRoot, "", ""); 215 } 216 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