1f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin/*
2f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin * Copyright (C) 2010 The Android Open Source Project
3f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin *
4f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin * Licensed under the Apache License, Version 2.0 (the "License");
5f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin * you may not use this file except in compliance with the License.
6f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin * You may obtain a copy of the License at
7f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin *
8f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin *      http://www.apache.org/licenses/LICENSE-2.0
9f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin *
10f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin * Unless required by applicable law or agreed to in writing, software
11f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin * distributed under the License is distributed on an "AS IS" BASIS,
12f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin * See the License for the specific language governing permissions and
14f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin * limitations under the License.
15f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin */
16f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
17f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Linpackage com.android.gallery3d.app;
18f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
19f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Linimport com.android.gallery3d.R;
20f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Linimport com.android.gallery3d.data.MediaObject;
21f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Linimport com.android.gallery3d.data.Path;
22f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
23f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin// This class handles filtering and clustering.
24f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin//
25f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin// We allow at most only one filter operation at a time (Currently it
26f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin// doesn't make sense to use more than one). Also each clustering operation
27f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin// can be applied at most once. In addition, there is one more constraint
28f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin// ("fixed set constraint") described below.
29f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin//
30f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin// A clustered album (not including album set) and its base sets are fixed.
31f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin// For example,
32f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin//
33f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin// /cluster/{base_set}/time/7
34f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin//
35f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin// This set and all sets inside base_set (recursively) are fixed because
36f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin// 1. We can not change this set to use another clustering condition (like
37f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin//    changing "time" to "location").
38f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin// 2. Neither can we change any set in the base_set.
39f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin// The reason is in both cases the 7th set may not exist in the new clustering.
40f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin// ---------------------
41f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin// newPath operation: create a new path based on a source path and put an extra
42f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin// condition on top of it:
43f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin//
44f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin// T = newFilterPath(S, filterType);
45f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin// T = newClusterPath(S, clusterType);
46f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin//
47f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin// Similar functions can be used to replace the current condition (if there is one).
48f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin//
49f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin// T = switchFilterPath(S, filterType);
50f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin// T = switchClusterPath(S, clusterType);
51f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin//
52f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin// For all fixed set in the path defined above, if some clusterType and
53f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin// filterType are already used, they cannot not be used as parameter for these
54f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin// functions. setupMenuItems() makes sure those types cannot be selected.
55f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin//
56f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Linpublic class FilterUtils {
57f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    private static final String TAG = "FilterUtils";
58f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
59f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    public static final int CLUSTER_BY_ALBUM = 1;
60f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    public static final int CLUSTER_BY_TIME = 2;
61f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    public static final int CLUSTER_BY_LOCATION = 4;
62f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    public static final int CLUSTER_BY_TAG = 8;
63f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    public static final int CLUSTER_BY_SIZE = 16;
64f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    public static final int CLUSTER_BY_FACE = 32;
65f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
66f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    public static final int FILTER_IMAGE_ONLY = 1;
67f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    public static final int FILTER_VIDEO_ONLY = 2;
68f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    public static final int FILTER_ALL = 4;
69f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
70f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    // These are indices of the return values of getAppliedFilters().
71f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    // The _F suffix means "fixed".
72f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    private static final int CLUSTER_TYPE = 0;
73f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    private static final int FILTER_TYPE = 1;
74f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    private static final int CLUSTER_TYPE_F = 2;
75f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    private static final int FILTER_TYPE_F = 3;
76f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    private static final int CLUSTER_CURRENT_TYPE = 4;
77f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    private static final int FILTER_CURRENT_TYPE = 5;
78f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
79f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    public static void setupMenuItems(GalleryActionBar model, Path path, boolean inAlbum) {
80f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        int[] result = new int[6];
81f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        getAppliedFilters(path, result);
82f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        int ctype = result[CLUSTER_TYPE];
83f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        int ftype = result[FILTER_TYPE];
84f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        int ftypef = result[FILTER_TYPE_F];
85f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        int ccurrent = result[CLUSTER_CURRENT_TYPE];
86f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        int fcurrent = result[FILTER_CURRENT_TYPE];
87f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
88f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        setMenuItemApplied(model, CLUSTER_BY_TIME,
89f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin                (ctype & CLUSTER_BY_TIME) != 0, (ccurrent & CLUSTER_BY_TIME) != 0);
90f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        setMenuItemApplied(model, CLUSTER_BY_LOCATION,
91f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin                (ctype & CLUSTER_BY_LOCATION) != 0, (ccurrent & CLUSTER_BY_LOCATION) != 0);
92f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        setMenuItemApplied(model, CLUSTER_BY_TAG,
93f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin                (ctype & CLUSTER_BY_TAG) != 0, (ccurrent & CLUSTER_BY_TAG) != 0);
94f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        setMenuItemApplied(model, CLUSTER_BY_FACE,
95f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin                (ctype & CLUSTER_BY_FACE) != 0, (ccurrent & CLUSTER_BY_FACE) != 0);
96f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
97f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        model.setClusterItemVisibility(CLUSTER_BY_ALBUM, !inAlbum || ctype == 0);
98f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
99f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        setMenuItemApplied(model, R.id.action_cluster_album, ctype == 0,
100f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin                ccurrent == 0);
101f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
102f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        // A filtering is available if it's not applied, and the old filtering
103f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        // (if any) is not fixed.
104f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        setMenuItemAppliedEnabled(model, R.string.show_images_only,
105f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin                (ftype & FILTER_IMAGE_ONLY) != 0,
106f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin                (ftype & FILTER_IMAGE_ONLY) == 0 && ftypef == 0,
107f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin                (fcurrent & FILTER_IMAGE_ONLY) != 0);
108f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        setMenuItemAppliedEnabled(model, R.string.show_videos_only,
109f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin                (ftype & FILTER_VIDEO_ONLY) != 0,
110f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin                (ftype & FILTER_VIDEO_ONLY) == 0 && ftypef == 0,
111f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin                (fcurrent & FILTER_VIDEO_ONLY) != 0);
112f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        setMenuItemAppliedEnabled(model, R.string.show_all,
113f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin                ftype == 0, ftype != 0 && ftypef == 0, fcurrent == 0);
114f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    }
115f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
116f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    // Gets the filters applied in the path.
117f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    private static void getAppliedFilters(Path path, int[] result) {
118f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        getAppliedFilters(path, result, false);
119f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    }
120f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
121f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    private static void getAppliedFilters(Path path, int[] result, boolean underCluster) {
122f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        String[] segments = path.split();
123f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        // Recurse into sub media sets.
124f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        for (int i = 0; i < segments.length; i++) {
125f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            if (segments[i].startsWith("{")) {
126f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin                String[] sets = Path.splitSequence(segments[i]);
127f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin                for (int j = 0; j < sets.length; j++) {
128f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin                    Path sub = Path.fromString(sets[j]);
129f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin                    getAppliedFilters(sub, result, underCluster);
130f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin                }
131f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            }
132f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        }
133f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
134f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        // update current selection
135f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        if (segments[0].equals("cluster")) {
136f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            // if this is a clustered album, set underCluster to true.
137f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            if (segments.length == 4) {
138f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin                underCluster = true;
139f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            }
140f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
141f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            int ctype = toClusterType(segments[2]);
142f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            result[CLUSTER_TYPE] |= ctype;
143f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            result[CLUSTER_CURRENT_TYPE] = ctype;
144f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            if (underCluster) {
145f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin                result[CLUSTER_TYPE_F] |= ctype;
146f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            }
147f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        }
148f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    }
149f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
150f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    private static int toClusterType(String s) {
151f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        if (s.equals("time")) {
152f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            return CLUSTER_BY_TIME;
153f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        } else if (s.equals("location")) {
154f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            return CLUSTER_BY_LOCATION;
155f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        } else if (s.equals("tag")) {
156f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            return CLUSTER_BY_TAG;
157f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        } else if (s.equals("size")) {
158f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            return CLUSTER_BY_SIZE;
159f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        } else if (s.equals("face")) {
160f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            return CLUSTER_BY_FACE;
161f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        }
162f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        return 0;
163f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    }
164f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
165f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    private static void setMenuItemApplied(
166f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            GalleryActionBar model, int id, boolean applied, boolean updateTitle) {
167f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        model.setClusterItemEnabled(id, !applied);
168f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    }
169f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
170f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    private static void setMenuItemAppliedEnabled(GalleryActionBar model, int id, boolean applied, boolean enabled, boolean updateTitle) {
171f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        model.setClusterItemEnabled(id, enabled);
172f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    }
173f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
174f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    // Add a specified filter to the path.
175f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    public static String newFilterPath(String base, int filterType) {
176f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        int mediaType;
177f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        switch (filterType) {
178f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            case FILTER_IMAGE_ONLY:
179f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin                mediaType = MediaObject.MEDIA_TYPE_IMAGE;
180f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin                break;
181f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            case FILTER_VIDEO_ONLY:
182f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin                mediaType = MediaObject.MEDIA_TYPE_VIDEO;
183f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin                break;
184f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            default:  /* FILTER_ALL */
185f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin                return base;
186f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        }
187f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
188f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        return "/filter/mediatype/" + mediaType + "/{" + base + "}";
189f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    }
190f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
191f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    // Add a specified clustering to the path.
192f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    public static String newClusterPath(String base, int clusterType) {
193f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        String kind;
194f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        switch (clusterType) {
195f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            case CLUSTER_BY_TIME:
196f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin                kind = "time";
197f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin                break;
198f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            case CLUSTER_BY_LOCATION:
199f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin                kind = "location";
200f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin                break;
201f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            case CLUSTER_BY_TAG:
202f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin                kind = "tag";
203f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin                break;
204f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            case CLUSTER_BY_SIZE:
205f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin                kind = "size";
206f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin                break;
207f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            case CLUSTER_BY_FACE:
208f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin                kind = "face";
209f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin                break;
210f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            default: /* CLUSTER_BY_ALBUM */
211f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin                return base;
212f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        }
213f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
214f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        return "/cluster/{" + base + "}/" + kind;
215f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    }
216f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
217f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    // Change the topmost filter to the specified type.
218f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    public static String switchFilterPath(String base, int filterType) {
219f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        return newFilterPath(removeOneFilterFromPath(base), filterType);
220f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    }
221f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
222f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    // Change the topmost clustering to the specified type.
223f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    public static String switchClusterPath(String base, int clusterType) {
224f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        return newClusterPath(removeOneClusterFromPath(base), clusterType);
225f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    }
226f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
227f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    // Remove the topmost clustering (if any) from the path.
228f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    private static String removeOneClusterFromPath(String base) {
229f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        boolean[] done = new boolean[1];
230f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        return removeOneClusterFromPath(base, done);
231f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    }
232f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
233f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    private static String removeOneClusterFromPath(String base, boolean[] done) {
234f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        if (done[0]) return base;
235f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
236f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        String[] segments = Path.split(base);
237f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        if (segments[0].equals("cluster")) {
238f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            done[0] = true;
239f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            return Path.splitSequence(segments[1])[0];
240f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        }
241f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
242f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        StringBuilder sb = new StringBuilder();
243f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        for (int i = 0; i < segments.length; i++) {
244f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            sb.append("/");
245f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            if (segments[i].startsWith("{")) {
246f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin                sb.append("{");
247f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin                String[] sets = Path.splitSequence(segments[i]);
248f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin                for (int j = 0; j < sets.length; j++) {
249f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin                    if (j > 0) {
250f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin                        sb.append(",");
251f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin                    }
252f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin                    sb.append(removeOneClusterFromPath(sets[j], done));
253f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin                }
254f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin                sb.append("}");
255f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            } else {
256f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin                sb.append(segments[i]);
257f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            }
258f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        }
259f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        return sb.toString();
260f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    }
261f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
262f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    // Remove the topmost filter (if any) from the path.
263f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    private static String removeOneFilterFromPath(String base) {
264f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        boolean[] done = new boolean[1];
265f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        return removeOneFilterFromPath(base, done);
266f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    }
267f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
268f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    private static String removeOneFilterFromPath(String base, boolean[] done) {
269f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        if (done[0]) return base;
270f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
271f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        String[] segments = Path.split(base);
272f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        if (segments[0].equals("filter") && segments[1].equals("mediatype")) {
273f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            done[0] = true;
274f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            return Path.splitSequence(segments[3])[0];
275f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        }
276f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
277f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        StringBuilder sb = new StringBuilder();
278f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        for (int i = 0; i < segments.length; i++) {
279f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            sb.append("/");
280f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            if (segments[i].startsWith("{")) {
281f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin                sb.append("{");
282f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin                String[] sets = Path.splitSequence(segments[i]);
283f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin                for (int j = 0; j < sets.length; j++) {
284f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin                    if (j > 0) {
285f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin                        sb.append(",");
286f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin                    }
287f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin                    sb.append(removeOneFilterFromPath(sets[j], done));
288f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin                }
289f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin                sb.append("}");
290f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            } else {
291f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin                sb.append(segments[i]);
292f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            }
293f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        }
294f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        return sb.toString();
295f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    }
296f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin}
297