Add ContentMetadata and AbstractContentMetadata
------------- Created by MOE: https://github.com/google/moe MOE_MIGRATED_REVID=188041418
This commit is contained in:
parent
73048f18c1
commit
f81dc44112
151
library/core/src/main/java/com/google/android/exoplayer2/upstream/cache/AbstractContentMetadata.java
vendored
Normal file
151
library/core/src/main/java/com/google/android/exoplayer2/upstream/cache/AbstractContentMetadata.java
vendored
Normal file
@ -0,0 +1,151 @@
|
||||
/*
|
||||
* Copyright (C) 2018 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.google.android.exoplayer2.upstream.cache;
|
||||
|
||||
import com.google.android.exoplayer2.C;
|
||||
import com.google.android.exoplayer2.upstream.cache.Cache.CacheException;
|
||||
import com.google.android.exoplayer2.util.Assertions;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.nio.charset.Charset;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
/** Abstract implementation of {@link ContentMetadata}. Values are stored as byte arrays. */
|
||||
public abstract class AbstractContentMetadata implements ContentMetadata {
|
||||
|
||||
private final Map<String, byte[]> metadata;
|
||||
|
||||
protected AbstractContentMetadata() {
|
||||
this.metadata = new HashMap<>();
|
||||
}
|
||||
|
||||
/** @param metadata Initial name value pairs. */
|
||||
protected AbstractContentMetadata(Map<String, byte[]> metadata) {
|
||||
this.metadata = new HashMap<>(metadata);
|
||||
}
|
||||
|
||||
@Override
|
||||
public final Editor edit() {
|
||||
return new EditorImpl();
|
||||
}
|
||||
|
||||
@Override
|
||||
public final byte[] get(String name, byte[] defaultValue) {
|
||||
synchronized (metadata) {
|
||||
if (metadata.containsKey(name)) {
|
||||
return metadata.get(name);
|
||||
} else {
|
||||
return defaultValue;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public final String get(String name, String defaultValue) {
|
||||
synchronized (metadata) {
|
||||
if (metadata.containsKey(name)) {
|
||||
byte[] bytes = metadata.get(name);
|
||||
return new String(bytes, Charset.forName(C.UTF8_NAME));
|
||||
} else {
|
||||
return defaultValue;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public final long get(String name, long defaultValue) {
|
||||
synchronized (metadata) {
|
||||
if (metadata.containsKey(name)) {
|
||||
byte[] bytes = metadata.get(name);
|
||||
return ByteBuffer.wrap(bytes).getLong();
|
||||
} else {
|
||||
return defaultValue;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public final boolean contains(String name) {
|
||||
synchronized (metadata) {
|
||||
return metadata.containsKey(name);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Called when any metadata value is changed or removed. {@code metadataValues} shouldn't be
|
||||
* accessed out of this method.
|
||||
*
|
||||
* @param metadataValues All metadata name, value pairs.
|
||||
*/
|
||||
protected abstract void onChange(Map<String, byte[]> metadataValues) throws CacheException;
|
||||
|
||||
private void apply(ArrayList<String> removedValues, Map<String, byte[]> editedValues)
|
||||
throws CacheException {
|
||||
synchronized (metadata) {
|
||||
for (int i = 0; i < removedValues.size(); i++) {
|
||||
metadata.remove(removedValues.get(i));
|
||||
}
|
||||
metadata.putAll(editedValues);
|
||||
onChange(Collections.unmodifiableMap(metadata));
|
||||
}
|
||||
}
|
||||
|
||||
private class EditorImpl implements Editor {
|
||||
|
||||
private final Map<String, byte[]> editedValues;
|
||||
private final ArrayList<String> removedValues;
|
||||
|
||||
private EditorImpl() {
|
||||
editedValues = new HashMap<>();
|
||||
removedValues = new ArrayList<>();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Editor set(String name, String value) {
|
||||
set(name, value.getBytes());
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Editor set(String name, long value) {
|
||||
set(name, ByteBuffer.allocate(8).putLong(value).array());
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Editor set(String name, byte[] value) {
|
||||
editedValues.put(name, Assertions.checkNotNull(value));
|
||||
removedValues.remove(name);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Editor remove(String name) {
|
||||
removedValues.add(name);
|
||||
editedValues.remove(name);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void commit() throws CacheException {
|
||||
apply(removedValues, editedValues);
|
||||
removedValues.clear();
|
||||
editedValues.clear();
|
||||
}
|
||||
}
|
||||
}
|
104
library/core/src/main/java/com/google/android/exoplayer2/upstream/cache/ContentMetadata.java
vendored
Normal file
104
library/core/src/main/java/com/google/android/exoplayer2/upstream/cache/ContentMetadata.java
vendored
Normal file
@ -0,0 +1,104 @@
|
||||
/*
|
||||
* Copyright (C) 2018 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.google.android.exoplayer2.upstream.cache;
|
||||
|
||||
import com.google.android.exoplayer2.upstream.cache.Cache.CacheException;
|
||||
|
||||
/** Interface for accessing cached content metadata which is stored as name, value pairs. */
|
||||
public interface ContentMetadata {
|
||||
|
||||
/**
|
||||
* Interface for modifying values in a {@link ContentMetadata} object. The changes you make in an
|
||||
* editor are not copied back to the original {@link ContentMetadata} until you call {@link
|
||||
* #commit()}.
|
||||
*/
|
||||
interface Editor {
|
||||
/**
|
||||
* Sets a metadata value, to be committed once {@link #commit()} is called. Passing {@code null}
|
||||
* as {@code value} isn't allowed. {@code value} byte array shouldn't be modified after passed
|
||||
* to this method.
|
||||
*
|
||||
* @param name The name of the metadata value.
|
||||
* @param value The value to be set.
|
||||
* @return This Editor instance, for convenience.
|
||||
*/
|
||||
Editor set(String name, byte[] value);
|
||||
/**
|
||||
* Sets a metadata value, to be committed once {@link #commit()} is called. Passing {@code null}
|
||||
* as value isn't allowed.
|
||||
*
|
||||
* @param name The name of the metadata value.
|
||||
* @param value The value to be set.
|
||||
* @return This Editor instance, for convenience.
|
||||
*/
|
||||
Editor set(String name, String value);
|
||||
/**
|
||||
* Sets a metadata value, to be committed once {@link #commit()} is called.
|
||||
*
|
||||
* @param name The name of the metadata value.
|
||||
* @param value The value to be set.
|
||||
* @return This Editor instance, for convenience.
|
||||
*/
|
||||
Editor set(String name, long value);
|
||||
/**
|
||||
* Sets a metadata value, to be committed once {@link #commit()} is called. Passing {@code null}
|
||||
* as value isn't allowed.
|
||||
*
|
||||
* @param name The name of the metadata value.
|
||||
* @return This Editor instance, for convenience.
|
||||
*/
|
||||
Editor remove(String name);
|
||||
/**
|
||||
* Commits changes. It can be called only once.
|
||||
*
|
||||
* @throws CacheException If the commit fails.
|
||||
*/
|
||||
void commit() throws CacheException;
|
||||
}
|
||||
|
||||
/** Returns an editor to change metadata values. */
|
||||
Editor edit();
|
||||
|
||||
/**
|
||||
* Returns a metadata value.
|
||||
*
|
||||
* @param name Name of the metadata to be returned.
|
||||
* @param defaultValue Value to return if the metadata doesn't exist.
|
||||
* @return The metadata value.
|
||||
*/
|
||||
byte[] get(String name, byte[] defaultValue);
|
||||
|
||||
/**
|
||||
* Returns a metadata value.
|
||||
*
|
||||
* @param name Name of the metadata to be returned.
|
||||
* @param defaultValue Value to return if the metadata doesn't exist.
|
||||
* @return The metadata value.
|
||||
*/
|
||||
String get(String name, String defaultValue);
|
||||
|
||||
/**
|
||||
* Returns a metadata value.
|
||||
*
|
||||
* @param name Name of the metadata to be returned.
|
||||
* @param defaultValue Value to return if the metadata doesn't exist.
|
||||
* @return The metadata value.
|
||||
*/
|
||||
long get(String name, long defaultValue);
|
||||
|
||||
/** Returns whether the metadata is available. */
|
||||
boolean contains(String name);
|
||||
}
|
@ -0,0 +1,188 @@
|
||||
/*
|
||||
* Copyright (C) 2018 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.google.android.exoplayer2.upstream.cache;
|
||||
|
||||
import static com.google.common.truth.Truth.assertThat;
|
||||
|
||||
import com.google.android.exoplayer2.C;
|
||||
import com.google.android.exoplayer2.upstream.cache.Cache.CacheException;
|
||||
import com.google.android.exoplayer2.upstream.cache.ContentMetadata.Editor;
|
||||
import java.nio.charset.Charset;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.robolectric.RobolectricTestRunner;
|
||||
|
||||
/** Tests {@link AbstractContentMetadata}. */
|
||||
@RunWith(RobolectricTestRunner.class)
|
||||
public class AbstractContentMetadataTest {
|
||||
|
||||
private FakeAbstractContentMetadata contentMetadata;
|
||||
|
||||
@Before
|
||||
public void setUp() throws Exception {
|
||||
contentMetadata = createAbstractContentMetadata();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testContainsReturnsFalseWhenEmpty() throws Exception {
|
||||
assertThat(contentMetadata.contains("test metadata")).isFalse();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testContainsReturnsTrueForInitialValue() throws Exception {
|
||||
contentMetadata = createAbstractContentMetadata("metadata name", "value");
|
||||
assertThat(contentMetadata.contains("metadata name")).isTrue();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetReturnsDefaultValueWhenValueIsNotAvailable() throws Exception {
|
||||
assertThat(contentMetadata.get("metadata name", "default value")).isEqualTo("default value");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetReturnsInitialValue() throws Exception {
|
||||
contentMetadata = createAbstractContentMetadata("metadata name", "value");
|
||||
assertThat(contentMetadata.get("metadata name", "default value")).isEqualTo("value");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testEditReturnsAnEditor() throws Exception {
|
||||
assertThat(contentMetadata.edit()).isNotNull();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testEditReturnsAnotherEditorEveryTime() throws Exception {
|
||||
assertThat(contentMetadata.edit()).isNotEqualTo(contentMetadata.edit());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCommitWithoutEditDoesNotFail() throws Exception {
|
||||
Editor editor = contentMetadata.edit();
|
||||
editor.commit();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testAddNewMetadata() throws Exception {
|
||||
Editor editor = contentMetadata.edit();
|
||||
editor.set("metadata name", "value");
|
||||
editor.commit();
|
||||
assertThat(contentMetadata.get("metadata name", "default value")).isEqualTo("value");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testAddNewIntMetadata() throws Exception {
|
||||
Editor editor = contentMetadata.edit();
|
||||
editor.set("metadata name", 5);
|
||||
editor.commit();
|
||||
assertThat(contentMetadata.get("metadata name", 0)).isEqualTo(5);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testAddNewByteArrayMetadata() throws Exception {
|
||||
Editor editor = contentMetadata.edit();
|
||||
byte[] value = {1, 2, 3};
|
||||
editor.set("metadata name", value);
|
||||
editor.commit();
|
||||
assertThat(contentMetadata.get("metadata name", new byte[] {})).isEqualTo(value);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testNewMetadataNotWrittenBeforeCommitted() throws Exception {
|
||||
Editor editor = contentMetadata.edit();
|
||||
editor.set("metadata name", "value");
|
||||
assertThat(contentMetadata.get("metadata name", "default value")).isEqualTo("default value");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testEditMetadata() throws Exception {
|
||||
contentMetadata = createAbstractContentMetadata("metadata name", "value");
|
||||
Editor editor = contentMetadata.edit();
|
||||
editor.set("metadata name", "edited value");
|
||||
editor.commit();
|
||||
assertThat(contentMetadata.get("metadata name", "default value")).isEqualTo("edited value");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testRemoveMetadata() throws Exception {
|
||||
contentMetadata = createAbstractContentMetadata("metadata name", "value");
|
||||
Editor editor = contentMetadata.edit();
|
||||
editor.remove("metadata name");
|
||||
editor.commit();
|
||||
assertThat(contentMetadata.get("metadata name", "default value")).isEqualTo("default value");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testAddAndRemoveMetadata() throws Exception {
|
||||
Editor editor = contentMetadata.edit();
|
||||
editor.set("metadata name", "value");
|
||||
editor.remove("metadata name");
|
||||
editor.commit();
|
||||
assertThat(contentMetadata.get("metadata name", "default value")).isEqualTo("default value");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testRemoveAndAddMetadata() throws Exception {
|
||||
Editor editor = contentMetadata.edit();
|
||||
editor.remove("metadata name");
|
||||
editor.set("metadata name", "value");
|
||||
editor.commit();
|
||||
assertThat(contentMetadata.get("metadata name", "default value")).isEqualTo("value");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testOnChangeIsCalledWhenMetadataEdited() throws Exception {
|
||||
contentMetadata =
|
||||
createAbstractContentMetadata(
|
||||
"metadata name", "value", "metadata name2", "value2", "metadata name3", "value3");
|
||||
Editor editor = contentMetadata.edit();
|
||||
editor.set("metadata name", "edited value");
|
||||
editor.remove("metadata name2");
|
||||
editor.commit();
|
||||
assertThat(contentMetadata.remainingValues).containsExactly("metadata name", "metadata name3");
|
||||
}
|
||||
|
||||
private FakeAbstractContentMetadata createAbstractContentMetadata(String... pairs) {
|
||||
assertThat(pairs.length % 2).isEqualTo(0);
|
||||
HashMap<String, byte[]> map = new HashMap<>();
|
||||
for (int i = 0; i < pairs.length; i += 2) {
|
||||
map.put(pairs[i], getBytes(pairs[i + 1]));
|
||||
}
|
||||
return new FakeAbstractContentMetadata(Collections.unmodifiableMap(map));
|
||||
}
|
||||
|
||||
private static byte[] getBytes(String value) {
|
||||
return value.getBytes(Charset.forName(C.UTF8_NAME));
|
||||
}
|
||||
|
||||
private static class FakeAbstractContentMetadata extends AbstractContentMetadata {
|
||||
|
||||
private ArrayList<String> remainingValues;
|
||||
|
||||
private FakeAbstractContentMetadata(Map<String, byte[]> metadataValues) {
|
||||
super(metadataValues);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onChange(Map<String, byte[]> metadataValues) throws CacheException {
|
||||
remainingValues = new ArrayList<>(metadataValues.keySet());
|
||||
}
|
||||
}
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user