/*
* Copyright (C) 2014 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.exoplayer.dash.mpd;
import com.google.android.exoplayer.util.Assertions;
import com.google.android.exoplayer.util.UriUtil;
import android.net.Uri;
/**
* Defines a range of data located at a {@link Uri}.
*/
public final class RangedUri {
/**
* The (zero based) index of the first byte of the range.
*/
public final long start;
/**
* The length of the range, or -1 to indicate that the range is unbounded.
*/
public final long length;
// The URI is stored internally in two parts: reference URI and a base URI to use when
// resolving it. This helps optimize memory usage in the same way that DASH manifests allow many
// URLs to be expressed concisely in the form of a single BaseURL and many relative paths. Note
// that this optimization relies on the same object being passed as the base URI to many
// instances of this class.
private final String baseUri;
private final String referenceUri;
private int hashCode;
/**
* Constructs an ranged uri.
*
* @param baseUri A uri that can form the base of the uri defined by the instance.
* @param referenceUri A reference uri that should be resolved with respect to {@code baseUri}.
* @param start The (zero based) index of the first byte of the range.
* @param length The length of the range, or -1 to indicate that the range is unbounded.
*/
public RangedUri(String baseUri, String referenceUri, long start, long length) {
Assertions.checkArgument(baseUri != null || referenceUri != null);
this.baseUri = baseUri;
this.referenceUri = referenceUri;
this.start = start;
this.length = length;
}
/**
* Returns the {@link Uri} represented by the instance.
*
* @return The {@link Uri} represented by the instance.
*/
public Uri getUri() {
return UriUtil.resolveToUri(baseUri, referenceUri);
}
/**
* Returns the uri represented by the instance as a string.
*
* @return The uri represented by the instance.
*/
public String getUriString() {
return UriUtil.resolve(baseUri, referenceUri);
}
/**
* Attempts to merge this {@link RangedUri} with another.
* <p>
* A merge is successful if both instances define the same {@link Uri}, and if one starte the
* byte after the other ends, forming a contiguous region with no overlap.
* <p>
* If {@code other} is null then the merge is considered unsuccessful, and null is returned.
*
* @param other The {@link RangedUri} to merge.
* @return The merged {@link RangedUri} if the merge was successful. Null otherwise.
*/
public RangedUri attemptMerge(RangedUri other) {
if (other == null || !getUriString().equals(other.getUriString())) {
return null;
} else if (length != -1 && start + length == other.start) {
return new RangedUri(baseUri, referenceUri, start,
other.length == -1 ? -1 : length + other.length);
} else if (other.length != -1 && other.start + other.length == start) {
return new RangedUri(baseUri, referenceUri, other.start,
length == -1 ? -1 : other.length + length);
} else {
return null;
}
}
@Override
public int hashCode() {
if (hashCode == 0) {
int result = 17;
result = 31 * result + (int) start;
result = 31 * result + (int) length;
result = 31 * result + getUriString().hashCode();
hashCode = result;
}
return hashCode;
}
@Override
public boolean equals(Object obj) {
if (this == obj) {
return true;
}
if (obj == null || getClass() != obj.getClass()) {
return false;
}
RangedUri other = (RangedUri) obj;
return this.start == other.start
&& this.length == other.length
&& getUriString().equals(other.getUriString());
}
}