/*
* Copyright (C) 2010 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.gallery3d.data;
import com.android.gallery3d.common.Utils;
import com.android.gallery3d.util.IdentityCache;
import java.lang.ref.WeakReference;
import java.util.ArrayList;
public class Path {
private static final String TAG = "Path";
private static Path sRoot = new Path(null, "ROOT");
private final Path mParent;
private final String mSegment;
private WeakReference<MediaObject> mObject;
private IdentityCache<String, Path> mChildren;
private Path(Path parent, String segment) {
mParent = parent;
mSegment = segment;
}
public Path getChild(String segment) {
synchronized (Path.class) {
if (mChildren == null) {
mChildren = new IdentityCache<String, Path>();
} else {
Path p = mChildren.get(segment);
if (p != null) return p;
}
Path p = new Path(this, segment);
mChildren.put(segment, p);
return p;
}
}
public Path getParent() {
synchronized (Path.class) {
return mParent;
}
}
public Path getChild(int segment) {
return getChild(String.valueOf(segment));
}
public Path getChild(long segment) {
return getChild(String.valueOf(segment));
}
public void setObject(MediaObject object) {
synchronized (Path.class) {
Utils.assertTrue(mObject == null || mObject.get() == null);
mObject = new WeakReference<MediaObject>(object);
}
}
MediaObject getObject() {
synchronized (Path.class) {
return (mObject == null) ? null : mObject.get();
}
}
@Override
// TODO: toString() should be more efficient, will fix it later
public String toString() {
synchronized (Path.class) {
StringBuilder sb = new StringBuilder();
String[] segments = split();
for (int i = 0; i < segments.length; i++) {
sb.append("/");
sb.append(segments[i]);
}
return sb.toString();
}
}
public boolean equalsIgnoreCase (String p) {
String path = toString();
return path.equalsIgnoreCase(p);
}
public static Path fromString(String s) {
synchronized (Path.class) {
String[] segments = split(s);
Path current = sRoot;
for (int i = 0; i < segments.length; i++) {
current = current.getChild(segments[i]);
}
return current;
}
}
public String[] split() {
synchronized (Path.class) {
int n = 0;
for (Path p = this; p != sRoot; p = p.mParent) {
n++;
}
String[] segments = new String[n];
int i = n - 1;
for (Path p = this; p != sRoot; p = p.mParent) {
segments[i--] = p.mSegment;
}
return segments;
}
}
public static String[] split(String s) {
int n = s.length();
if (n == 0) return new String[0];
if (s.charAt(0) != '/') {
throw new RuntimeException("malformed path:" + s);
}
ArrayList<String> segments = new ArrayList<String>();
int i = 1;
while (i < n) {
int brace = 0;
int j;
for (j = i; j < n; j++) {
char c = s.charAt(j);
if (c == '{') ++brace;
else if (c == '}') --brace;
else if (brace == 0 && c == '/') break;
}
if (brace != 0) {
throw new RuntimeException("unbalanced brace in path:" + s);
}
segments.add(s.substring(i, j));
i = j + 1;
}
String[] result = new String[segments.size()];
segments.toArray(result);
return result;
}
// Splits a string to an array of strings.
// For example, "{foo,bar,baz}" -> {"foo","bar","baz"}.
public static String[] splitSequence(String s) {
int n = s.length();
if (s.charAt(0) != '{' || s.charAt(n-1) != '}') {
throw new RuntimeException("bad sequence: " + s);
}
ArrayList<String> segments = new ArrayList<String>();
int i = 1;
while (i < n - 1) {
int brace = 0;
int j;
for (j = i; j < n - 1; j++) {
char c = s.charAt(j);
if (c == '{') ++brace;
else if (c == '}') --brace;
else if (brace == 0 && c == ',') break;
}
if (brace != 0) {
throw new RuntimeException("unbalanced brace in path:" + s);
}
segments.add(s.substring(i, j));
i = j + 1;
}
String[] result = new String[segments.size()];
segments.toArray(result);
return result;
}
public String getPrefix() {
if (this == sRoot) return "";
return getPrefixPath().mSegment;
}
public Path getPrefixPath() {
synchronized (Path.class) {
Path current = this;
if (current == sRoot) {
throw new IllegalStateException();
}
while (current.mParent != sRoot) {
current = current.mParent;
}
return current;
}
}
public String getSuffix() {
// We don't need lock because mSegment is final.
return mSegment;
}
// Below are for testing/debugging only
static void clearAll() {
synchronized (Path.class) {
sRoot = new Path(null, "");
}
}
static void dumpAll() {
dumpAll(sRoot, "", "");
}
static void dumpAll(Path p, String prefix1, String prefix2) {
synchronized (Path.class) {
MediaObject obj = p.getObject();
Log.d(TAG, prefix1 + p.mSegment + ":"
+ (obj == null ? "null" : obj.getClass().getSimpleName()));
if (p.mChildren != null) {
ArrayList<String> childrenKeys = p.mChildren.keys();
int i = 0, n = childrenKeys.size();
for (String key : childrenKeys) {
Path child = p.mChildren.get(key);
if (child == null) {
++i;
continue;
}
Log.d(TAG, prefix2 + "|");
if (++i < n) {
dumpAll(child, prefix2 + "+-- ", prefix2 + "| ");
} else {
dumpAll(child, prefix2 + "+-- ", prefix2 + " ");
}
}
}
}
}
}