/* * Copyright (C) 2017 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.dialer.common.database; import android.support.annotation.NonNull; import android.support.annotation.Nullable; import com.android.dialer.common.Assert; import java.util.ArrayList; import java.util.Collections; import java.util.List; /** * Utility to build SQL selections. Handles string concatenation, nested statements, empty * statements, and tracks the selection arguments. * *
A selection can be build from a string, factory methods like {@link #column(String)}, or use * {@link Builder} to build complex nested selection with multiple operators. The Selection manages * the {@code selection} and {@code selectionArgs} passed into {@link * android.content.ContentResolver#query(android.net.Uri, String[], String, String[], String)}. * *
Example: * *
* fromString("foo = 1")
*
*
* expands into "(foo = 1)", {}
*
* * *
* column("foo").is("LIKE", "bar")
*
*
* expands into "(foo LIKE ?)", {"bar"}
*
* * *
* builder()
* .and(
* fromString("foo = ?", "1").buildUpon()
* .or(column("bar").is("<", 2))
* .build())
* .and(not(column("baz").is("!= 3")))
* .build();
*
*
* expands into "(((foo = ?) OR (bar < ?)) AND (NOT (baz != 3)))", {"1", "2"}
*/
public final class Selection {
private final String selection;
private final String[] selectionArgs;
private Selection(@NonNull String selection, @NonNull String[] selectionArgs) {
this.selection = selection;
this.selectionArgs = selectionArgs;
}
@NonNull
public String getSelection() {
return selection;
}
@NonNull
public String[] getSelectionArgs() {
return selectionArgs;
}
public boolean isEmpty() {
return selection.isEmpty();
}
/**
* @return a mutable builder that appends to the selection. The selection will be parenthesized
* before anything is appended to it.
*/
@NonNull
public Builder buildUpon() {
return new Builder(this);
}
/** @return a builder that is empty. */
@NonNull
public static Builder builder() {
return new Builder();
}
/**
* @return a Selection built from regular selection string/args pair. The result selection will be
* enclosed in a parenthesis.
*/
@NonNull
public static Selection fromString(@Nullable String selection, @Nullable String... args) {
return new Builder(selection, args).build();
}
/** @return a selection that is negated */
@NonNull
public static Selection not(@NonNull Selection selection) {
Assert.checkArgument(!selection.isEmpty());
return fromString("NOT " + selection.getSelection(), selection.getSelectionArgs());
}
/**
* Build a selection based on condition upon a column. is() should be called to complete the
* selection.
*/
@NonNull
public static Column column(@NonNull String column) {
return new Column(column);
}
/** Helper class to build a selection based on condition upon a column. */
public static class Column {
@NonNull private final String column;
private Column(@NonNull String column) {
this.column = Assert.isNotNull(column);
}
/** Expands to "