diff options
6 files changed, 414 insertions, 77 deletions
diff --git a/apex/appsearch/framework/java/external/android/app/appsearch/AppSearchBatchResult.java b/apex/appsearch/framework/java/external/android/app/appsearch/AppSearchBatchResult.java index 272e12db0124..d493a1c28a3a 100644 --- a/apex/appsearch/framework/java/external/android/app/appsearch/AppSearchBatchResult.java +++ b/apex/appsearch/framework/java/external/android/app/appsearch/AppSearchBatchResult.java @@ -96,17 +96,6 @@ public final class AppSearchBatchResult<KeyType, ValueType> { return Collections.unmodifiableMap(mAll); } - /** - * Asserts that this {@link AppSearchBatchResult} has no failures. - * - * @hide - */ - public void checkSuccess() { - if (!isSuccess()) { - throw new IllegalStateException("AppSearchBatchResult has failures: " + this); - } - } - @Override @NonNull public String toString() { diff --git a/apex/appsearch/framework/java/external/android/app/appsearch/AppSearchSchema.java b/apex/appsearch/framework/java/external/android/app/appsearch/AppSearchSchema.java index 237e6242b22a..0ee5e65ef775 100644 --- a/apex/appsearch/framework/java/external/android/app/appsearch/AppSearchSchema.java +++ b/apex/appsearch/framework/java/external/android/app/appsearch/AppSearchSchema.java @@ -21,6 +21,7 @@ import android.annotation.NonNull; import android.annotation.Nullable; import android.app.appsearch.exceptions.IllegalSchemaException; import android.app.appsearch.util.BundleUtil; +import android.app.appsearch.util.IndentingStringBuilder; import android.compat.annotation.UnsupportedAppUsage; import android.os.Bundle; import android.util.ArraySet; @@ -30,6 +31,7 @@ import com.android.internal.util.Preconditions; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.util.ArrayList; +import java.util.Arrays; import java.util.Collections; import java.util.List; import java.util.Objects; @@ -67,8 +69,45 @@ public final class AppSearchSchema { } @Override + @NonNull public String toString() { - return mBundle.toString(); + IndentingStringBuilder stringBuilder = new IndentingStringBuilder(); + appendAppSearchSchemaString(stringBuilder); + return stringBuilder.toString(); + } + + /** + * Appends a debugging string for the {@link AppSearchSchema} instance to the given string + * builder. + * + * @param builder the builder to append to. + */ + private void appendAppSearchSchemaString(@NonNull IndentingStringBuilder builder) { + Objects.requireNonNull(builder); + + builder.append("{\n"); + builder.increaseIndentLevel(); + builder.append("schemaType: \"").append(getSchemaType()).append("\",\n"); + builder.append("properties: [\n"); + + AppSearchSchema.PropertyConfig[] sortedProperties = + getProperties().toArray(new AppSearchSchema.PropertyConfig[0]); + Arrays.sort(sortedProperties, (o1, o2) -> o1.getName().compareTo(o2.getName())); + + for (int i = 0; i < sortedProperties.length; i++) { + AppSearchSchema.PropertyConfig propertyConfig = sortedProperties[i]; + builder.increaseIndentLevel(); + propertyConfig.appendPropertyConfigString(builder); + if (i != sortedProperties.length - 1) { + builder.append(",\n"); + } + builder.decreaseIndentLevel(); + } + + builder.append("\n"); + builder.append("]\n"); + builder.decreaseIndentLevel(); + builder.append("}"); } /** Returns the name of this schema type, e.g. Email. */ @@ -255,7 +294,68 @@ public final class AppSearchSchema { @Override @NonNull public String toString() { - return mBundle.toString(); + IndentingStringBuilder stringBuilder = new IndentingStringBuilder(); + appendPropertyConfigString(stringBuilder); + return stringBuilder.toString(); + } + + /** + * Appends a debug string for the {@link AppSearchSchema.PropertyConfig} instance to the + * given string builder. + * + * @param builder the builder to append to. + */ + void appendPropertyConfigString(@NonNull IndentingStringBuilder builder) { + Objects.requireNonNull(builder); + + builder.append("{\n"); + builder.increaseIndentLevel(); + builder.append("name: \"").append(getName()).append("\",\n"); + + if (this instanceof AppSearchSchema.StringPropertyConfig) { + ((StringPropertyConfig) this).appendStringPropertyConfigFields(builder); + } else if (this instanceof AppSearchSchema.DocumentPropertyConfig) { + ((DocumentPropertyConfig) this).appendDocumentPropertyConfigFields(builder); + } + + switch (getCardinality()) { + case AppSearchSchema.PropertyConfig.CARDINALITY_REPEATED: + builder.append("cardinality: CARDINALITY_REPEATED,\n"); + break; + case AppSearchSchema.PropertyConfig.CARDINALITY_OPTIONAL: + builder.append("cardinality: CARDINALITY_OPTIONAL,\n"); + break; + case AppSearchSchema.PropertyConfig.CARDINALITY_REQUIRED: + builder.append("cardinality: CARDINALITY_REQUIRED,\n"); + break; + default: + builder.append("cardinality: CARDINALITY_UNKNOWN,\n"); + } + + switch (getDataType()) { + case AppSearchSchema.PropertyConfig.DATA_TYPE_STRING: + builder.append("dataType: DATA_TYPE_STRING,\n"); + break; + case AppSearchSchema.PropertyConfig.DATA_TYPE_LONG: + builder.append("dataType: DATA_TYPE_LONG,\n"); + break; + case AppSearchSchema.PropertyConfig.DATA_TYPE_DOUBLE: + builder.append("dataType: DATA_TYPE_DOUBLE,\n"); + break; + case AppSearchSchema.PropertyConfig.DATA_TYPE_BOOLEAN: + builder.append("dataType: DATA_TYPE_BOOLEAN,\n"); + break; + case AppSearchSchema.PropertyConfig.DATA_TYPE_BYTES: + builder.append("dataType: DATA_TYPE_BYTES,\n"); + break; + case AppSearchSchema.PropertyConfig.DATA_TYPE_DOCUMENT: + builder.append("dataType: DATA_TYPE_DOCUMENT,\n"); + break; + default: + builder.append("dataType: DATA_TYPE_UNKNOWN,\n"); + } + builder.decreaseIndentLevel(); + builder.append("}"); } /** Returns the name of this property. */ @@ -506,6 +606,41 @@ public final class AppSearchSchema { return new StringPropertyConfig(bundle); } } + + /** + * Appends a debug string for the {@link StringPropertyConfig} instance to the given string + * builder. + * + * <p>This appends fields specific to a {@link StringPropertyConfig} instance. + * + * @param builder the builder to append to. + */ + void appendStringPropertyConfigFields(@NonNull IndentingStringBuilder builder) { + switch (getIndexingType()) { + case AppSearchSchema.StringPropertyConfig.INDEXING_TYPE_NONE: + builder.append("indexingType: INDEXING_TYPE_NONE,\n"); + break; + case AppSearchSchema.StringPropertyConfig.INDEXING_TYPE_EXACT_TERMS: + builder.append("indexingType: INDEXING_TYPE_EXACT_TERMS,\n"); + break; + case AppSearchSchema.StringPropertyConfig.INDEXING_TYPE_PREFIXES: + builder.append("indexingType: INDEXING_TYPE_PREFIXES,\n"); + break; + default: + builder.append("indexingType: INDEXING_TYPE_UNKNOWN,\n"); + } + + switch (getTokenizerType()) { + case AppSearchSchema.StringPropertyConfig.TOKENIZER_TYPE_NONE: + builder.append("tokenizerType: TOKENIZER_TYPE_NONE,\n"); + break; + case AppSearchSchema.StringPropertyConfig.TOKENIZER_TYPE_PLAIN: + builder.append("tokenizerType: TOKENIZER_TYPE_PLAIN,\n"); + break; + default: + builder.append("tokenizerType: TOKENIZER_TYPE_UNKNOWN,\n"); + } + } } /** @@ -858,5 +993,21 @@ public final class AppSearchSchema { return new DocumentPropertyConfig(bundle); } } + + /** + * Appends a debug string for the {@link DocumentPropertyConfig} instance to the given + * string builder. + * + * <p>This appends fields specific to a {@link DocumentPropertyConfig} instance. + * + * @param builder the builder to append to. + */ + void appendDocumentPropertyConfigFields(@NonNull IndentingStringBuilder builder) { + builder.append("shouldIndexNestedProperties: ") + .append(shouldIndexNestedProperties()) + .append(",\n"); + + builder.append("schemaType: \"").append(getSchemaType()).append("\",\n"); + } } } diff --git a/apex/appsearch/framework/java/external/android/app/appsearch/GenericDocument.java b/apex/appsearch/framework/java/external/android/app/appsearch/GenericDocument.java index e688cb3c9be9..c905f95fe4c4 100644 --- a/apex/appsearch/framework/java/external/android/app/appsearch/GenericDocument.java +++ b/apex/appsearch/framework/java/external/android/app/appsearch/GenericDocument.java @@ -22,6 +22,7 @@ import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.SuppressLint; import android.app.appsearch.util.BundleUtil; +import android.app.appsearch.util.IndentingStringBuilder; import android.compat.annotation.UnsupportedAppUsage; import android.os.Bundle; import android.os.Parcelable; @@ -858,6 +859,7 @@ public class GenericDocument { * * @hide */ + // TODO(b/171882200): Expose this API in Android T @NonNull public GenericDocument.Builder<GenericDocument.Builder<?>> toBuilder() { Bundle clonedBundle = BundleUtil.deepCopy(mBundle); @@ -887,121 +889,101 @@ public class GenericDocument { @Override @NonNull public String toString() { - StringBuilder stringBuilder = new StringBuilder(); - appendGenericDocumentString(this, /*indentLevel=*/ 0, stringBuilder); + IndentingStringBuilder stringBuilder = new IndentingStringBuilder(); + appendGenericDocumentString(stringBuilder); return stringBuilder.toString(); } - private static void appendGenericDocumentString( - @NonNull GenericDocument document, int indentLevel, @NonNull StringBuilder builder) { - Objects.requireNonNull(document); + /** + * Appends a debug string for the {@link GenericDocument} instance to the given string builder. + * + * @param builder the builder to append to. + */ + void appendGenericDocumentString(@NonNull IndentingStringBuilder builder) { Objects.requireNonNull(builder); - builder.append(getIndent(indentLevel)).append("{\n"); - - String indent1 = getIndent(indentLevel + 1); - - builder.append(indent1) - .append("namespace: \"") - .append(document.getNamespace()) - .append("\",\n"); - - builder.append(indent1).append("id: \"").append(document.getId()).append("\",\n"); - - builder.append(indent1).append("score: ").append(document.getScore()).append(",\n"); - - builder.append(indent1) - .append("schemaType: \"") - .append(document.getSchemaType()) - .append("\",\n"); + builder.append("{\n"); + builder.increaseIndentLevel(); - builder.append(indent1) - .append("creationTimestampMillis: ") - .append(document.getCreationTimestampMillis()) + builder.append("namespace: \"").append(getNamespace()).append("\",\n"); + builder.append("id: \"").append(getId()).append("\",\n"); + builder.append("score: ").append(getScore()).append(",\n"); + builder.append("schemaType: \"").append(getSchemaType()).append("\",\n"); + builder.append("creationTimestampMillis: ") + .append(getCreationTimestampMillis()) .append(",\n"); + builder.append("timeToLiveMillis: ").append(getTtlMillis()).append(",\n"); - builder.append(indent1) - .append("timeToLiveMillis: ") - .append(document.getTtlMillis()) - .append(",\n"); - - builder.append(indent1).append("properties: {\n"); + builder.append("properties: {\n"); - String[] sortedProperties = document.getPropertyNames().toArray(new String[0]); + String[] sortedProperties = getPropertyNames().toArray(new String[0]); Arrays.sort(sortedProperties); for (int i = 0; i < sortedProperties.length; i++) { - Object property = document.getProperty(sortedProperties[i]); - builder.append(getIndent(indentLevel + 2)) - .append("\"") - .append(sortedProperties[i]) - .append("\"") - .append(": "); - appendPropertyString(property, indentLevel + 2, builder); + Object property = getProperty(sortedProperties[i]); + builder.increaseIndentLevel(); + appendPropertyString(sortedProperties[i], property, builder); if (i != sortedProperties.length - 1) { builder.append(",\n"); } + builder.decreaseIndentLevel(); } builder.append("\n"); - builder.append(indent1).append("}"); + builder.append("}"); + builder.decreaseIndentLevel(); builder.append("\n"); - builder.append(getIndent(indentLevel)).append("}"); + builder.append("}"); } /** - * Appends a string for the given property to the given builder. + * Appends a debug string for the given document property to the given string builder. * + * @param propertyName name of property to create string for. * @param property property object to create string for. - * @param indentLevel base indent level for property. * @param builder the builder to append to. */ - private static void appendPropertyString( - @NonNull Object property, int indentLevel, @NonNull StringBuilder builder) { + private void appendPropertyString( + @NonNull String propertyName, + @NonNull Object property, + @NonNull IndentingStringBuilder builder) { + Objects.requireNonNull(propertyName); Objects.requireNonNull(property); Objects.requireNonNull(builder); - builder.append("["); + builder.append("\"").append(propertyName).append("\": ["); if (property instanceof GenericDocument[]) { GenericDocument[] documentValues = (GenericDocument[]) property; for (int i = 0; i < documentValues.length; ++i) { builder.append("\n"); - appendGenericDocumentString(documentValues[i], indentLevel + 1, builder); + builder.increaseIndentLevel(); + documentValues[i].appendGenericDocumentString(builder); if (i != documentValues.length - 1) { - builder.append(", "); + builder.append(","); } builder.append("\n"); + builder.decreaseIndentLevel(); } - builder.append(getIndent(indentLevel)); + builder.append("]"); } else { int propertyArrLength = Array.getLength(property); for (int i = 0; i < propertyArrLength; i++) { Object propertyElement = Array.get(property, i); if (propertyElement instanceof String) { - builder.append("\"").append(propertyElement).append("\""); + builder.append("\"").append((String) propertyElement).append("\""); } else if (propertyElement instanceof byte[]) { builder.append(Arrays.toString((byte[]) propertyElement)); } else { - builder.append(propertyElement); + builder.append(propertyElement.toString()); } if (i != propertyArrLength - 1) { builder.append(", "); + } else { + builder.append("]"); } } } - - builder.append("]"); - } - - /** Appends a string for given indent level to the given builder. */ - @NonNull - private static String getIndent(int indentLevel) { - StringBuilder builder = new StringBuilder(); - for (int i = 0; i < indentLevel; ++i) { - builder.append(" "); - } - return builder.toString(); } /** diff --git a/apex/appsearch/framework/java/external/android/app/appsearch/util/IndentingStringBuilder.java b/apex/appsearch/framework/java/external/android/app/appsearch/util/IndentingStringBuilder.java new file mode 100644 index 000000000000..b494c3ce9337 --- /dev/null +++ b/apex/appsearch/framework/java/external/android/app/appsearch/util/IndentingStringBuilder.java @@ -0,0 +1,126 @@ +/* + * Copyright 2021 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 android.app.appsearch.util; + +import android.annotation.NonNull; + +/** + * Utility for building indented strings. + * + * <p>This is a wrapper for {@link StringBuilder} for appending strings with indentation. The + * indentation level can be increased by calling {@link #increaseIndentLevel()} and decreased by + * calling {@link #decreaseIndentLevel()}. + * + * <p>Indentation is applied after each newline character for the given indent level. + * + * @hide + */ +public class IndentingStringBuilder { + private final StringBuilder mStringBuilder = new StringBuilder(); + + // Indicates whether next non-newline character should have an indent applied before it. + private boolean mIndentNext = false; + private int mIndentLevel = 0; + + /** Increases the indent level by one for appended strings. */ + @NonNull + public IndentingStringBuilder increaseIndentLevel() { + mIndentLevel++; + return this; + } + + /** Decreases the indent level by one for appended strings. */ + @NonNull + public IndentingStringBuilder decreaseIndentLevel() throws IllegalStateException { + if (mIndentLevel == 0) { + throw new IllegalStateException("Cannot set indent level below 0."); + } + mIndentLevel--; + return this; + } + + /** + * Appends provided {@code String} at the current indentation level. + * + * <p>Indentation is applied after each newline character. + */ + @NonNull + public IndentingStringBuilder append(@NonNull String str) { + applyIndentToString(str); + return this; + } + + /** + * Appends provided {@code Object}, represented as a {@code String}, at the current indentation + * level. + * + * <p>Indentation is applied after each newline character. + */ + @NonNull + public IndentingStringBuilder append(@NonNull Object obj) { + applyIndentToString(obj.toString()); + return this; + } + + @Override + @NonNull + public String toString() { + return mStringBuilder.toString(); + } + + /** Adds indent string to the {@link StringBuilder} instance for current indent level. */ + private void applyIndent() { + for (int i = 0; i < mIndentLevel; i++) { + mStringBuilder.append(" "); + } + } + + /** + * Applies indent, for current indent level, after each newline character. + * + * <p>Consecutive newline characters are not indented. + */ + private void applyIndentToString(@NonNull String str) { + int index = str.indexOf("\n"); + if (index == 0) { + // String begins with new line character: append newline and slide past newline. + mStringBuilder.append("\n"); + mIndentNext = true; + if (str.length() > 1) { + applyIndentToString(str.substring(index + 1)); + } + } else if (index >= 1) { + // String contains new line character: divide string between newline, append new line, + // and recurse on each string. + String beforeIndentString = str.substring(0, index); + applyIndentToString(beforeIndentString); + mStringBuilder.append("\n"); + mIndentNext = true; + if (str.length() > index + 1) { + String afterIndentString = str.substring(index + 1); + applyIndentToString(afterIndentString); + } + } else { + // String does not contain newline character: append string. + if (mIndentNext) { + applyIndent(); + mIndentNext = false; + } + mStringBuilder.append(str); + } + } +} diff --git a/apex/appsearch/synced_jetpack_changeid.txt b/apex/appsearch/synced_jetpack_changeid.txt index 970d3f1796a0..65551074f9c0 100644 --- a/apex/appsearch/synced_jetpack_changeid.txt +++ b/apex/appsearch/synced_jetpack_changeid.txt @@ -1 +1 @@ -a11281766f66aa4919e9bbe70da95919ce054c35 +c7387d9b58726a23a0608a9365fb3a1b57b7b8a1 diff --git a/core/tests/coretests/src/android/app/appsearch/external/util/IndentingStringBuilderTest.java b/core/tests/coretests/src/android/app/appsearch/external/util/IndentingStringBuilderTest.java new file mode 100644 index 000000000000..057ecbd9dfe9 --- /dev/null +++ b/core/tests/coretests/src/android/app/appsearch/external/util/IndentingStringBuilderTest.java @@ -0,0 +1,89 @@ +/* + * Copyright 2021 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 android.app.appsearch.util; + +import static com.google.common.truth.Truth.assertThat; + +import static org.junit.Assert.assertThrows; + +import org.junit.Test; + +public class IndentingStringBuilderTest { + @Test + public void testAppendIndentedStrings() { + IndentingStringBuilder stringBuilder = new IndentingStringBuilder(); + stringBuilder + .increaseIndentLevel() + .append("\nIndentLevel1\nIndentLevel1\n") + .decreaseIndentLevel() + .append("IndentLevel0,\n"); + + String str = stringBuilder.toString(); + String expectedString = "\n IndentLevel1\n IndentLevel1\nIndentLevel0,\n"; + + assertThat(str).isEqualTo(expectedString); + } + + @Test + public void testDecreaseIndentLevel_throwsException() { + IndentingStringBuilder stringBuilder = new IndentingStringBuilder(); + + Exception e = + assertThrows( + IllegalStateException.class, () -> stringBuilder.decreaseIndentLevel()); + assertThat(e).hasMessageThat().contains("Cannot set indent level below 0."); + } + + @Test + public void testAppendIndentedObjects() { + IndentingStringBuilder stringBuilder = new IndentingStringBuilder(); + Object stringProperty = "String"; + Object longProperty = 1L; + Object booleanProperty = true; + + stringBuilder + .append(stringProperty) + .append("\n") + .increaseIndentLevel() + .append(longProperty) + .append("\n") + .decreaseIndentLevel() + .append(booleanProperty); + + String str = stringBuilder.toString(); + String expectedString = "String\n 1\ntrue"; + + assertThat(str).isEqualTo(expectedString); + } + + @Test + public void testAppendIndentedStrings_doesNotIndentLineBreak() { + IndentingStringBuilder stringBuilder = new IndentingStringBuilder(); + + stringBuilder + .append("\n") + .increaseIndentLevel() + .append("\n\n") + .decreaseIndentLevel() + .append("\n"); + + String str = stringBuilder.toString(); + String expectedString = "\n\n\n\n"; + + assertThat(str).isEqualTo(expectedString); + } +} |