• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright 2020 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.server.appsearch.external.localstorage.converter;
18 
19 import android.annotation.NonNull;
20 import android.app.appsearch.AppSearchSchema;
21 import android.util.Log;
22 
23 import com.google.android.icing.proto.DocumentIndexingConfig;
24 import com.google.android.icing.proto.PropertyConfigProto;
25 import com.google.android.icing.proto.SchemaTypeConfigProto;
26 import com.google.android.icing.proto.SchemaTypeConfigProtoOrBuilder;
27 import com.google.android.icing.proto.StringIndexingConfig;
28 import com.google.android.icing.proto.TermMatchType;
29 
30 import java.util.List;
31 import java.util.Objects;
32 
33 /**
34  * Translates an {@link AppSearchSchema} into a {@link SchemaTypeConfigProto}.
35  *
36  * @hide
37  */
38 public final class SchemaToProtoConverter {
39     private static final String TAG = "AppSearchSchemaToProtoC";
40 
SchemaToProtoConverter()41     private SchemaToProtoConverter() {}
42 
43     /**
44      * Converts an {@link android.app.appsearch.AppSearchSchema} into a {@link
45      * SchemaTypeConfigProto}.
46      */
47     @NonNull
toSchemaTypeConfigProto( @onNull AppSearchSchema schema, int version)48     public static SchemaTypeConfigProto toSchemaTypeConfigProto(
49             @NonNull AppSearchSchema schema, int version) {
50         Objects.requireNonNull(schema);
51         SchemaTypeConfigProto.Builder protoBuilder =
52                 SchemaTypeConfigProto.newBuilder()
53                         .setSchemaType(schema.getSchemaType())
54                         .setVersion(version);
55         List<AppSearchSchema.PropertyConfig> properties = schema.getProperties();
56         for (int i = 0; i < properties.size(); i++) {
57             PropertyConfigProto propertyProto = toPropertyConfigProto(properties.get(i));
58             protoBuilder.addProperties(propertyProto);
59         }
60         return protoBuilder.build();
61     }
62 
63     @NonNull
toPropertyConfigProto( @onNull AppSearchSchema.PropertyConfig property)64     private static PropertyConfigProto toPropertyConfigProto(
65             @NonNull AppSearchSchema.PropertyConfig property) {
66         Objects.requireNonNull(property);
67         PropertyConfigProto.Builder builder =
68                 PropertyConfigProto.newBuilder().setPropertyName(property.getName());
69 
70         // Set dataType
71         @AppSearchSchema.PropertyConfig.DataType int dataType = property.getDataType();
72         PropertyConfigProto.DataType.Code dataTypeProto =
73                 PropertyConfigProto.DataType.Code.forNumber(dataType);
74         if (dataTypeProto == null) {
75             throw new IllegalArgumentException("Invalid dataType: " + dataType);
76         }
77         builder.setDataType(dataTypeProto);
78 
79         // Set cardinality
80         @AppSearchSchema.PropertyConfig.Cardinality int cardinality = property.getCardinality();
81         PropertyConfigProto.Cardinality.Code cardinalityProto =
82                 PropertyConfigProto.Cardinality.Code.forNumber(cardinality);
83         if (cardinalityProto == null) {
84             throw new IllegalArgumentException("Invalid cardinality: " + dataType);
85         }
86         builder.setCardinality(cardinalityProto);
87 
88         if (property instanceof AppSearchSchema.StringPropertyConfig) {
89             AppSearchSchema.StringPropertyConfig stringProperty =
90                     (AppSearchSchema.StringPropertyConfig) property;
91             StringIndexingConfig stringIndexingConfig =
92                     StringIndexingConfig.newBuilder()
93                             .setTermMatchType(
94                                     convertTermMatchTypeToProto(stringProperty.getIndexingType()))
95                             .setTokenizerType(
96                                     convertTokenizerTypeToProto(stringProperty.getTokenizerType()))
97                             .build();
98             builder.setStringIndexingConfig(stringIndexingConfig);
99 
100         } else if (property instanceof AppSearchSchema.DocumentPropertyConfig) {
101             AppSearchSchema.DocumentPropertyConfig documentProperty =
102                     (AppSearchSchema.DocumentPropertyConfig) property;
103             builder.setSchemaType(documentProperty.getSchemaType())
104                     .setDocumentIndexingConfig(
105                             DocumentIndexingConfig.newBuilder()
106                                     .setIndexNestedProperties(
107                                             documentProperty.shouldIndexNestedProperties()));
108         }
109         return builder.build();
110     }
111 
112     /**
113      * Converts a {@link SchemaTypeConfigProto} into an {@link
114      * android.app.appsearch.AppSearchSchema}.
115      */
116     @NonNull
toAppSearchSchema(@onNull SchemaTypeConfigProtoOrBuilder proto)117     public static AppSearchSchema toAppSearchSchema(@NonNull SchemaTypeConfigProtoOrBuilder proto) {
118         Objects.requireNonNull(proto);
119         AppSearchSchema.Builder builder = new AppSearchSchema.Builder(proto.getSchemaType());
120         List<PropertyConfigProto> properties = proto.getPropertiesList();
121         for (int i = 0; i < properties.size(); i++) {
122             AppSearchSchema.PropertyConfig propertyConfig = toPropertyConfig(properties.get(i));
123             builder.addProperty(propertyConfig);
124         }
125         return builder.build();
126     }
127 
128     @NonNull
toPropertyConfig( @onNull PropertyConfigProto proto)129     private static AppSearchSchema.PropertyConfig toPropertyConfig(
130             @NonNull PropertyConfigProto proto) {
131         Objects.requireNonNull(proto);
132         switch (proto.getDataType()) {
133             case STRING:
134                 return toStringPropertyConfig(proto);
135             case INT64:
136                 return new AppSearchSchema.LongPropertyConfig.Builder(proto.getPropertyName())
137                         .setCardinality(proto.getCardinality().getNumber())
138                         .build();
139             case DOUBLE:
140                 return new AppSearchSchema.DoublePropertyConfig.Builder(proto.getPropertyName())
141                         .setCardinality(proto.getCardinality().getNumber())
142                         .build();
143             case BOOLEAN:
144                 return new AppSearchSchema.BooleanPropertyConfig.Builder(proto.getPropertyName())
145                         .setCardinality(proto.getCardinality().getNumber())
146                         .build();
147             case BYTES:
148                 return new AppSearchSchema.BytesPropertyConfig.Builder(proto.getPropertyName())
149                         .setCardinality(proto.getCardinality().getNumber())
150                         .build();
151             case DOCUMENT:
152                 return toDocumentPropertyConfig(proto);
153             default:
154                 throw new IllegalArgumentException("Invalid dataType: " + proto.getDataType());
155         }
156     }
157 
158     @NonNull
toStringPropertyConfig( @onNull PropertyConfigProto proto)159     private static AppSearchSchema.StringPropertyConfig toStringPropertyConfig(
160             @NonNull PropertyConfigProto proto) {
161         AppSearchSchema.StringPropertyConfig.Builder builder =
162                 new AppSearchSchema.StringPropertyConfig.Builder(proto.getPropertyName())
163                         .setCardinality(proto.getCardinality().getNumber())
164                         .setTokenizerType(
165                                 proto.getStringIndexingConfig().getTokenizerType().getNumber());
166 
167         // Set indexingType
168         TermMatchType.Code termMatchTypeProto = proto.getStringIndexingConfig().getTermMatchType();
169         builder.setIndexingType(convertTermMatchTypeFromProto(termMatchTypeProto));
170 
171         return builder.build();
172     }
173 
174     @NonNull
toDocumentPropertyConfig( @onNull PropertyConfigProto proto)175     private static AppSearchSchema.DocumentPropertyConfig toDocumentPropertyConfig(
176             @NonNull PropertyConfigProto proto) {
177         return new AppSearchSchema.DocumentPropertyConfig.Builder(
178                         proto.getPropertyName(), proto.getSchemaType())
179                 .setCardinality(proto.getCardinality().getNumber())
180                 .setShouldIndexNestedProperties(
181                         proto.getDocumentIndexingConfig().getIndexNestedProperties())
182                 .build();
183     }
184 
185     @NonNull
convertTermMatchTypeToProto( @ppSearchSchema.StringPropertyConfig.IndexingType int indexingType)186     private static TermMatchType.Code convertTermMatchTypeToProto(
187             @AppSearchSchema.StringPropertyConfig.IndexingType int indexingType) {
188         switch (indexingType) {
189             case AppSearchSchema.StringPropertyConfig.INDEXING_TYPE_NONE:
190                 return TermMatchType.Code.UNKNOWN;
191             case AppSearchSchema.StringPropertyConfig.INDEXING_TYPE_EXACT_TERMS:
192                 return TermMatchType.Code.EXACT_ONLY;
193             case AppSearchSchema.StringPropertyConfig.INDEXING_TYPE_PREFIXES:
194                 return TermMatchType.Code.PREFIX;
195             default:
196                 throw new IllegalArgumentException("Invalid indexingType: " + indexingType);
197         }
198     }
199 
200     @AppSearchSchema.StringPropertyConfig.IndexingType
convertTermMatchTypeFromProto(@onNull TermMatchType.Code termMatchType)201     private static int convertTermMatchTypeFromProto(@NonNull TermMatchType.Code termMatchType) {
202         switch (termMatchType) {
203             case UNKNOWN:
204                 return AppSearchSchema.StringPropertyConfig.INDEXING_TYPE_NONE;
205             case EXACT_ONLY:
206                 return AppSearchSchema.StringPropertyConfig.INDEXING_TYPE_EXACT_TERMS;
207             case PREFIX:
208                 return AppSearchSchema.StringPropertyConfig.INDEXING_TYPE_PREFIXES;
209             default:
210                 // Avoid crashing in the 'read' path; we should try to interpret the document to the
211                 // extent possible.
212                 Log.w(TAG, "Invalid indexingType: " + termMatchType.getNumber());
213                 return AppSearchSchema.StringPropertyConfig.INDEXING_TYPE_NONE;
214         }
215     }
216 
217     @NonNull
convertTokenizerTypeToProto( @ppSearchSchema.StringPropertyConfig.TokenizerType int tokenizerType)218     private static StringIndexingConfig.TokenizerType.Code convertTokenizerTypeToProto(
219             @AppSearchSchema.StringPropertyConfig.TokenizerType int tokenizerType) {
220         StringIndexingConfig.TokenizerType.Code tokenizerTypeProto =
221                 StringIndexingConfig.TokenizerType.Code.forNumber(tokenizerType);
222         if (tokenizerTypeProto == null) {
223             throw new IllegalArgumentException("Invalid tokenizerType: " + tokenizerType);
224         }
225         return tokenizerTypeProto;
226     }
227 }
228