1 /* 2 * Copyright (C) 2011 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.providers.contacts; 18 19 import android.content.UriMatcher; 20 import android.net.Uri; 21 import android.provider.ContactsContract; 22 23 import com.google.android.collect.Lists; 24 import com.google.android.collect.Maps; 25 26 import java.util.List; 27 import java.util.Map; 28 import java.util.regex.Pattern; 29 30 /** 31 * A subclass of URI matcher with additional logic and awareness around profile-specific URIs. 32 */ 33 public class ProfileAwareUriMatcher extends UriMatcher { 34 35 private static final Pattern PATH_SPLIT_PATTERN = Pattern.compile("/"); 36 37 private static final String PROFILE_SEGMENT = "profile"; 38 private static final String LOOKUP_SEGMENT = "lookup"; 39 private static final String VCARD_SEGMENT = "as_vcard"; 40 private static final String ID_SEGMENT = "#"; 41 private static final String WILDCARD_SEGMENT = "*"; 42 43 // URIs matching to these constants must always use the profile database. 44 private static final List<Integer> PROFILE_URIS = Lists.newArrayList(); 45 46 // URIs in this map will direct to the profile database if the ID (which is at the segment 47 // path with the location specified by the value in the map) is in the profile ID-space. 48 private static final Map<Integer, Integer> PROFILE_URI_ID_MAP = Maps.newHashMap(); 49 50 // URIs in this map will direct to the profile database if the lookup key (which is at the 51 // segment path with the location specified by the value in the map) is the special profile 52 // lookup key (see {@link ProfileAggregator#PROFILE_LOOKUP_KEY}). 53 private static final Map<Integer, Integer> PROFILE_URI_LOOKUP_KEY_MAP = Maps.newHashMap(); 54 55 /** 56 * Creates the root node of the URI tree. 57 * 58 * @param code the code to match for the root URI 59 */ 60 public ProfileAwareUriMatcher(int code) { 61 super(code); 62 } 63 64 @Override 65 public void addURI(String authority, String path, int code) { 66 super.addURI(authority, path, code); 67 68 // Do a second tokenization pass to determine whether the URI may apply to profiles. 69 if (path != null) { 70 String[] tokens = PATH_SPLIT_PATTERN.split(path); 71 if (tokens != null) { 72 73 // Keep track of whether we've passed a "lookup" token in the path; wildcards after 74 // that token will be interpreted as lookup keys. For our purposes, vcard paths 75 // also count as lookup tokens, since the vcard is specified by lookup key. 76 boolean afterLookup = false; 77 for (int i = 0; i < tokens.length; i++) { 78 String token = tokens[i]; 79 if (token.equals(PROFILE_SEGMENT)) { 80 PROFILE_URIS.add(code); 81 return; 82 } else if (token.equals(LOOKUP_SEGMENT) 83 || token.equals(VCARD_SEGMENT)) { 84 afterLookup = true; 85 continue; 86 } else if (token.equals(ID_SEGMENT)) { 87 PROFILE_URI_ID_MAP.put(code, i); 88 } else if (token.equals(WILDCARD_SEGMENT)) { 89 if (afterLookup) { 90 PROFILE_URI_LOOKUP_KEY_MAP.put(code, i); 91 } 92 } 93 afterLookup = false; 94 } 95 } 96 } 97 } 98 99 /** 100 * Determines whether the given URI is intended for the profile DB rather than contacts. 101 * This is true under any of three conditions: 102 * 1. The URI itself is specifically for the profile (it contains a "profile" segment). 103 * 2. The URI contains ID references that are in the profile ID-space. 104 * 3. The URI contains lookup key references that match the special profile lookup key. 105 * @param uri The URI to examine. 106 * @return Whether the operation for this URI is intended for the profile DB. 107 */ 108 public boolean mapsToProfile(Uri uri) { 109 int match = match(uri); 110 if (PROFILE_URIS.contains(match)) { 111 return true; 112 } else if (PROFILE_URI_ID_MAP.containsKey(match)) { 113 int idSegment = PROFILE_URI_ID_MAP.get(match); 114 long id = Long.parseLong(uri.getPathSegments().get(idSegment)); 115 if (ContactsContract.isProfileId(id)) { 116 return true; 117 } 118 } else if (PROFILE_URI_LOOKUP_KEY_MAP.containsKey(match)) { 119 int lookupKeySegment = PROFILE_URI_LOOKUP_KEY_MAP.get(match); 120 String lookupKey = uri.getPathSegments().get(lookupKeySegment); 121 if (ContactLookupKey.PROFILE_LOOKUP_KEY.equals(lookupKey)) { 122 return true; 123 } 124 } 125 return false; 126 } 127 } 128